mirror of
				https://github.com/2dust/v2rayN.git
				synced 2025-10-31 04:22:51 +00:00 
			
		
		
		
	Compare commits
	
		
			54 commits
		
	
	
		
			a898c57518
			...
			739a5aeaa2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 739a5aeaa2 | ||
|   | 50dcae9a04 | ||
|   | 2910f4838e | ||
|   | bee0521e47 | ||
|   | 15c3eea92e | ||
|   | 147e837bbb | ||
|   | 35f9e4bca0 | ||
|   | 8ed5c575fc | ||
|   | f6b6926419 | ||
|   | 3298286f01 | ||
|   | 8ec94fe812 | ||
|   | 372e832651 | ||
|   | 3e322b4d68 | ||
|   | 2b46857763 | ||
|   | 94afd9969a | ||
|   | db708d1e5d | ||
|   | 6462fa0905 | ||
|   | 8deb897f55 | ||
|   | e28f5ce9c2 | ||
|   | 45a2ff118e | ||
|   | daf59d01dc | ||
|   | 815a9ce0db | ||
|   | edbd8ab54a | ||
|   | d2169c914a | ||
|   | 47779e433d | ||
|   | c5f222bf58 | ||
|   | 1945d9b798 | ||
|   | 2e283eb00e | ||
|   | 00505c7c17 | ||
|   | 9229b491c5 | ||
|   | d9b0aff8da | ||
|   | 8ea39d7475 | ||
|   | f14f39f5a1 | ||
|   | 228069f499 | ||
|   | 645e4e7711 | ||
|   | d1c16ecb72 | ||
|   | dca737bdad | ||
|   | 7f5489c5f7 | ||
|   | 3be0b312d6 | ||
|   | b290a38ef4 | ||
|   | 70bdb3de16 | ||
|   | 8f9d1951a2 | ||
|   | e2faf61a8b | ||
|   | 3824879d38 | ||
|   | d54433aeb3 | ||
|   | 26b2240779 | ||
|   | 98a5caa47f | ||
|   | d77c25aef5 | ||
|   | 7affcf97b1 | ||
|   | 9e1e5eb2aa | ||
|   | d1928d80c7 | ||
|   | ea55dfb6c5 | ||
|   | b3acb89c29 | ||
|   | 922cf54d93 | 
					 59 changed files with 3968 additions and 599 deletions
				
			
		|  | @ -466,11 +466,11 @@ public class Utils | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static int GetFreePort(int defaultPort = 9090) |     public static int GetFreePort(int defaultPort = 0) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             if (!Utils.PortInUse(defaultPort)) |             if (!(defaultPort == 0 || Utils.PortInUse(defaultPort))) | ||||||
|             { |             { | ||||||
|                 return defaultPort; |                 return defaultPort; | ||||||
|             } |             } | ||||||
|  | @ -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"); |         var tempPath = Path.Combine(StartupPath(), "binConfigs"); | ||||||
|         if (!Directory.Exists(tempPath)) |         if (!Directory.Exists(tempPath)) | ||||||
|  | @ -827,10 +827,27 @@ public class Utils | ||||||
|         } |         } | ||||||
|         else |         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 |     #endregion TempPath | ||||||
| 
 | 
 | ||||||
|     #region Platform |     #region Platform | ||||||
|  |  | ||||||
|  | @ -11,5 +11,10 @@ public enum EConfigType | ||||||
|     Hysteria2 = 7, |     Hysteria2 = 7, | ||||||
|     TUIC = 8, |     TUIC = 8, | ||||||
|     WireGuard = 9, |     WireGuard = 9, | ||||||
|     HTTP = 10 |     HTTP = 10, | ||||||
|  |     Anytls = 11, | ||||||
|  |     NaiveProxy = 100, | ||||||
|  |     Juicity = 101, | ||||||
|  |     Brook = 102, | ||||||
|  |     Shadowquic = 103, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,5 +9,6 @@ public enum EInboundProtocol | ||||||
|     api, |     api, | ||||||
|     api2, |     api2, | ||||||
|     mixed, |     mixed, | ||||||
|  |     split, | ||||||
|     speedtest = 21 |     speedtest = 21 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,8 +12,8 @@ public class Global | ||||||
| 
 | 
 | ||||||
|     public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; |     public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; | ||||||
|     public const string ConfigFileName = "guiNConfig.json"; |     public const string ConfigFileName = "guiNConfig.json"; | ||||||
|     public const string CoreConfigFileName = "config.json"; |     public const string CoreConfigFileName = "config"; | ||||||
|     public const string CorePreConfigFileName = "configPre.json"; |     public const string CorePreConfigFileName = "configPre"; | ||||||
|     public const string CoreSpeedtestConfigFileName = "configTest{0}.json"; |     public const string CoreSpeedtestConfigFileName = "configTest{0}.json"; | ||||||
|     public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; |     public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; | ||||||
|     public const string ClashMixinConfigFileName = "Mixin.yaml"; |     public const string ClashMixinConfigFileName = "Mixin.yaml"; | ||||||
|  | @ -169,7 +169,12 @@ public class Global | ||||||
|             { EConfigType.Trojan, "trojan://" }, |             { EConfigType.Trojan, "trojan://" }, | ||||||
|             { EConfigType.Hysteria2, "hysteria2://" }, |             { EConfigType.Hysteria2, "hysteria2://" }, | ||||||
|             { EConfigType.TUIC, "tuic://" }, |             { EConfigType.TUIC, "tuic://" }, | ||||||
|             { EConfigType.WireGuard, "wireguard://" } |             { EConfigType.WireGuard, "wireguard://" }, | ||||||
|  |             { EConfigType.Anytls, "anytls://" }, | ||||||
|  |             { EConfigType.NaiveProxy, "naive://" }, | ||||||
|  |             { EConfigType.Juicity, "juicity://" }, | ||||||
|  |             { EConfigType.Brook, "brook://" }, | ||||||
|  |             { EConfigType.Shadowquic, "shadowquic://" } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|     public static readonly Dictionary<EConfigType, string> ProtocolTypes = new() |     public static readonly Dictionary<EConfigType, string> ProtocolTypes = new() | ||||||
|  | @ -182,7 +187,12 @@ public class Global | ||||||
|             { EConfigType.Trojan, "trojan" }, |             { EConfigType.Trojan, "trojan" }, | ||||||
|             { EConfigType.Hysteria2, "hysteria2" }, |             { EConfigType.Hysteria2, "hysteria2" }, | ||||||
|             { EConfigType.TUIC, "tuic" }, |             { EConfigType.TUIC, "tuic" }, | ||||||
|             { EConfigType.WireGuard, "wireguard" } |             { EConfigType.WireGuard, "wireguard" }, | ||||||
|  |             { EConfigType.Anytls, "anytls" }, | ||||||
|  |             { EConfigType.NaiveProxy, "naiveproxy" }, | ||||||
|  |             { EConfigType.Juicity, "juicity" }, | ||||||
|  |             { EConfigType.Brook, "brook" }, | ||||||
|  |             { EConfigType.Shadowquic, "shadowquic" } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|     public static readonly List<string> VmessSecurities = |     public static readonly List<string> VmessSecurities = | ||||||
|  | @ -276,6 +286,75 @@ public class Global | ||||||
|             "sing_box" |             "sing_box" | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|  |     public static readonly List<string> Hysteria2CoreTypes = | ||||||
|  |     [ | ||||||
|  |         "sing_box", | ||||||
|  |             "hysteria2" | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly List<string> TuicCoreTypes = | ||||||
|  |     [ | ||||||
|  |         "sing_box", | ||||||
|  |             "tuic" | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly List<string> NaiveProxyCoreTypes = | ||||||
|  |     [ | ||||||
|  |         "naiveproxy" | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly List<string> JuicityProxyCoreTypes = | ||||||
|  |     [ | ||||||
|  |         "juicity" | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly List<string> BrookCoreTypes = | ||||||
|  |     [ | ||||||
|  |         "brook" | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly List<string> ShadowquicCoreTypes = | ||||||
|  |     [ | ||||||
|  |         "shadowquic" | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly List<EConfigType> SupportSplitConfigTypes = | ||||||
|  |     [ | ||||||
|  |         EConfigType.VMess, | ||||||
|  |             EConfigType.VLESS, | ||||||
|  |             EConfigType.Shadowsocks, | ||||||
|  |             EConfigType.Trojan, | ||||||
|  |             EConfigType.Hysteria2, | ||||||
|  |             EConfigType.TUIC, | ||||||
|  |             EConfigType.WireGuard, | ||||||
|  |             EConfigType.SOCKS, | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly HashSet<EConfigType> XraySupportConfigType = | ||||||
|  |     [ | ||||||
|  |         EConfigType.VMess, | ||||||
|  |             EConfigType.VLESS, | ||||||
|  |             EConfigType.Shadowsocks, | ||||||
|  |             EConfigType.Trojan, | ||||||
|  |             EConfigType.WireGuard, | ||||||
|  |             EConfigType.SOCKS, | ||||||
|  |             EConfigType.HTTP, | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     public static readonly HashSet<EConfigType> 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<string> DomainStrategies = |     public static readonly List<string> DomainStrategies = | ||||||
|     [ |     [ | ||||||
|         "AsIs", |         "AsIs", | ||||||
|  | @ -462,13 +541,20 @@ public class Global | ||||||
|             "" |             "" | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     public static readonly List<string> TuicCongestionControls = |     public static readonly List<string> CongestionControls = | ||||||
|     [ |     [ | ||||||
|         "cubic", |         "cubic", | ||||||
|             "new_reno", |             "new_reno", | ||||||
|             "bbr" |             "bbr" | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|  |     public static readonly List<string> NaiveProxyProtocols = | ||||||
|  |     [ | ||||||
|  |         "https", | ||||||
|  |             "http", | ||||||
|  |             "quic" | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|     public static readonly List<string> allowSelectType = |     public static readonly List<string> allowSelectType = | ||||||
|     [ |     [ | ||||||
|         "selector", |         "selector", | ||||||
|  |  | ||||||
|  | @ -1,3 +1,7 @@ | ||||||
|  | using DynamicData; | ||||||
|  | using ServiceLib.Enums; | ||||||
|  | using ServiceLib.Models; | ||||||
|  | 
 | ||||||
| namespace ServiceLib.Handler; | namespace ServiceLib.Handler; | ||||||
| 
 | 
 | ||||||
| public sealed class AppHandler | public sealed class AppHandler | ||||||
|  | @ -231,9 +235,89 @@ public sealed class AppHandler | ||||||
|             return (ECoreType)profileItem.CoreType; |             return (ECoreType)profileItem.CoreType; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         return GetCoreType(eConfigType); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public ECoreType GetCoreType(EConfigType eConfigType) | ||||||
|  |     { | ||||||
|         var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType); |         var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType); | ||||||
|         return item?.CoreType ?? ECoreType.Xray; |         return item?.CoreType ?? ECoreType.Xray; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public ECoreType GetSplitCoreType(ProfileItem profileItem, EConfigType eConfigType) | ||||||
|  |     { | ||||||
|  |         if (profileItem?.CoreType != null) | ||||||
|  |         { | ||||||
|  |             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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public (bool, ECoreType, ECoreType?) GetCoreAndPreType(ProfileItem profileItem) | ||||||
|  |     { | ||||||
|  |         var splitCore = _config.SplitCoreItem.EnableSplitCore; | ||||||
|  |         var coreType = GetCoreType(profileItem, profileItem.ConfigType); | ||||||
|  |         ECoreType? preCoreType = null; | ||||||
|  | 
 | ||||||
|  |         var pureEndpointCore = profileItem.CoreType ?? 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.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 |     #endregion Core Type | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -165,6 +165,13 @@ public class ConfigHandler | ||||||
|             config.SystemProxyItem.SystemProxyExceptions = Utils.IsWindows() ? Global.SystemProxyExceptionsWindows : Global.SystemProxyExceptionsLinux; |             config.SystemProxyItem.SystemProxyExceptions = Utils.IsWindows() ? Global.SystemProxyExceptionsWindows : Global.SystemProxyExceptionsLinux; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         config.SplitCoreItem ??= new() | ||||||
|  |         { | ||||||
|  |             EnableSplitCore = false, | ||||||
|  |             SplitCoreTypes = new List<CoreTypeItem>(), | ||||||
|  |             RouteCoreType = ECoreType.Xray | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         return config; |         return config; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -262,6 +269,11 @@ public class ConfigHandler | ||||||
|             EConfigType.Hysteria2 => await AddHysteria2Server(config, item), |             EConfigType.Hysteria2 => await AddHysteria2Server(config, item), | ||||||
|             EConfigType.TUIC => await AddTuicServer(config, item), |             EConfigType.TUIC => await AddTuicServer(config, item), | ||||||
|             EConfigType.WireGuard => await AddWireguardServer(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, |             _ => -1, | ||||||
|         }; |         }; | ||||||
|         return ret; |         return ret; | ||||||
|  | @ -690,7 +702,7 @@ public class ConfigHandler | ||||||
|     public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true) |     public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true) | ||||||
|     { |     { | ||||||
|         profileItem.ConfigType = EConfigType.Hysteria2; |         profileItem.ConfigType = EConfigType.Hysteria2; | ||||||
|         profileItem.CoreType = ECoreType.sing_box; |         //profileItem.CoreType = ECoreType.sing_box; | ||||||
| 
 | 
 | ||||||
|         profileItem.Address = profileItem.Address.TrimEx(); |         profileItem.Address = profileItem.Address.TrimEx(); | ||||||
|         profileItem.Id = profileItem.Id.TrimEx(); |         profileItem.Id = profileItem.Id.TrimEx(); | ||||||
|  | @ -723,16 +735,16 @@ public class ConfigHandler | ||||||
|     public static async Task<int> AddTuicServer(Config config, ProfileItem profileItem, bool toFile = true) |     public static async Task<int> AddTuicServer(Config config, ProfileItem profileItem, bool toFile = true) | ||||||
|     { |     { | ||||||
|         profileItem.ConfigType = EConfigType.TUIC; |         profileItem.ConfigType = EConfigType.TUIC; | ||||||
|         profileItem.CoreType = ECoreType.sing_box; |         //profileItem.CoreType = ECoreType.sing_box; | ||||||
| 
 | 
 | ||||||
|         profileItem.Address = profileItem.Address.TrimEx(); |         profileItem.Address = profileItem.Address.TrimEx(); | ||||||
|         profileItem.Id = profileItem.Id.TrimEx(); |         profileItem.Id = profileItem.Id.TrimEx(); | ||||||
|         profileItem.Security = profileItem.Security.TrimEx(); |         profileItem.Security = profileItem.Security.TrimEx(); | ||||||
|         profileItem.Network = string.Empty; |         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()) |         if (profileItem.StreamSecurity.IsNullOrEmpty()) | ||||||
|  | @ -786,6 +798,173 @@ public class ConfigHandler | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Add or edit a Anytls server | ||||||
|  |     /// Validates and processes Anytls-specific settings | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="config">Current configuration</param> | ||||||
|  |     /// <param name="profileItem">Anytls profile to add</param> | ||||||
|  |     /// <param name="toFile">Whether to save to file</param> | ||||||
|  |     /// <returns>0 if successful, -1 if failed</returns> | ||||||
|  |     public static async Task<int> 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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Add or edit a Naive server | ||||||
|  |     /// Validates and processes Naive-specific settings | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="config">Current configuration</param> | ||||||
|  |     /// <param name="profileItem">Naive profile to add</param> | ||||||
|  |     /// <param name="toFile">Whether to save to file</param> | ||||||
|  |     /// <returns>0 if successful, -1 if failed</returns> | ||||||
|  |     public static async Task<int> 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 (!Global.NaiveProxyProtocols.Contains(profileItem.HeaderType)) | ||||||
|  |         { | ||||||
|  |             profileItem.HeaderType = Global.NaiveProxyProtocols.FirstOrDefault()!; | ||||||
|  |         } | ||||||
|  |         if (profileItem.StreamSecurity.IsNullOrEmpty()) | ||||||
|  |         { | ||||||
|  |             profileItem.StreamSecurity = Global.StreamSecurity; | ||||||
|  |         } | ||||||
|  |         if (profileItem.Id.IsNullOrEmpty()) | ||||||
|  |         { | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |         await AddServerCommon(config, profileItem, toFile); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Add or edit a Juicity server | ||||||
|  |     /// Validates and processes Juicity-specific settings | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="config">Current configuration</param> | ||||||
|  |     /// <param name="profileItem">Juicity profile to add</param> | ||||||
|  |     /// <param name="toFile">Whether to save to file</param> | ||||||
|  |     /// <returns>0 if successful, -1 if failed</returns> | ||||||
|  |     public static async Task<int> 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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Add or edit a Brook server | ||||||
|  |     /// Validates and processes Brook-specific settings | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="config">Current configuration</param> | ||||||
|  |     /// <param name="profileItem">Brook profile to add</param> | ||||||
|  |     /// <param name="toFile">Whether to save to file</param> | ||||||
|  |     /// <returns>0 if successful, -1 if failed</returns> | ||||||
|  |     public static async Task<int> 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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Add or edit a Shadowquic server | ||||||
|  |     /// Validates and processes Shadowquic-specific settings | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="config">Current configuration</param> | ||||||
|  |     /// <param name="profileItem">Shadowquic profile to add</param> | ||||||
|  |     /// <param name="toFile">Whether to save to file</param> | ||||||
|  |     /// <returns>0 if successful, -1 if failed</returns> | ||||||
|  |     public static async Task<int> 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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Sort the server list by the specified column |     /// Sort the server list by the specified column | ||||||
|     /// Updates the sort order in the profile extension data |     /// Updates the sort order in the profile extension data | ||||||
|  | @ -1160,43 +1339,6 @@ public class ConfigHandler | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// <summary> |  | ||||||
|     /// 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 |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="config">Current configuration</param> |  | ||||||
|     /// <param name="node">Server node that might need pre-SOCKS</param> |  | ||||||
|     /// <param name="coreType">Core type being used</param> |  | ||||||
|     /// <returns>A SOCKS profile item or null if not needed</returns> |  | ||||||
|     public static async Task<ProfileItem?> 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; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Remove servers with invalid test results (timeout) |     /// Remove servers with invalid test results (timeout) | ||||||
|     /// Useful for cleaning up subscription lists |     /// Useful for cleaning up subscription lists | ||||||
|  | @ -1295,6 +1437,11 @@ public class ConfigHandler | ||||||
|                 EConfigType.Hysteria2 => await AddHysteria2Server(config, profileItem, false), |                 EConfigType.Hysteria2 => await AddHysteria2Server(config, profileItem, false), | ||||||
|                 EConfigType.TUIC => await AddTuicServer(config, profileItem, false), |                 EConfigType.TUIC => await AddTuicServer(config, profileItem, false), | ||||||
|                 EConfigType.WireGuard => await AddWireguardServer(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, |                 _ => -1, | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ public class CoreAdminHandler | ||||||
|     { |     { | ||||||
|         StringBuilder sb = new(); |         StringBuilder sb = new(); | ||||||
|         sb.AppendLine("#!/bin/bash"); |         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}"); |         sb.AppendLine($"sudo -S {cmdLine}"); | ||||||
|         var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); |         var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | using ServiceLib.Services.CoreConfig.Minimal; | ||||||
|  | 
 | ||||||
| namespace ServiceLib.Handler; | namespace ServiceLib.Handler; | ||||||
| 
 | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
|  | @ -7,27 +9,26 @@ public class CoreConfigHandler | ||||||
| { | { | ||||||
|     private static readonly string _tag = "CoreConfigHandler"; |     private static readonly string _tag = "CoreConfigHandler"; | ||||||
| 
 | 
 | ||||||
|     public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName) |     public static async Task<RetResult> GenerateClientConfig(CoreLaunchContext context, string? fileName) | ||||||
|     { |     { | ||||||
|         var config = AppHandler.Instance.Config; |  | ||||||
|         var result = new RetResult(); |         var result = new RetResult(); | ||||||
| 
 | 
 | ||||||
|         if (node.ConfigType == EConfigType.Custom) |         if (context.ConfigType == EConfigType.Custom) | ||||||
|         { |         { | ||||||
|             result = node.CoreType switch |             result = await GetCoreConfigServiceForCustom(context.CoreType).GenerateClientCustomConfig(context.Node, fileName); | ||||||
|             { |  | ||||||
|                 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) |  | ||||||
|         { |  | ||||||
|             result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node); |  | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node); |             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) |         if (result.Success != true) | ||||||
|         { |         { | ||||||
|  | @ -41,65 +42,44 @@ public class CoreConfigHandler | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName) |     public static async Task<RetResult> GeneratePassthroughConfig(CoreLaunchContext context, string? fileName) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var result = new RetResult(); | ||||||
|  | 
 | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             if (node == null || fileName is null) |             result = await GetCoreConfigServiceForPassthrough(context.CoreType).GeneratePassthroughConfig(context.Node); | ||||||
|             { |  | ||||||
|                 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) |         catch (Exception ex) | ||||||
|         { |         { | ||||||
|             Logging.SaveLog(_tag, ex); |             Logging.SaveLog(_tag, ex); | ||||||
|             ret.Msg = ResUI.FailedGenDefaultConfiguration; |             result.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|             return ret; |             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<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType) |     public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType) | ||||||
|     { |     { | ||||||
|         var result = new RetResult(); |         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) |         if (result.Success != true) | ||||||
|         { |         { | ||||||
|  | @ -109,21 +89,24 @@ public class CoreConfigHandler | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName) |     public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, CoreLaunchContext context, ServerTestItem testItem, string fileName) | ||||||
|     { |     { | ||||||
|         var result = new RetResult(); |         var result = new RetResult(); | ||||||
|         var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); |         var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); | ||||||
|         var port = Utils.GetFreePort(initPort + testItem.QueueNum); |         var port = Utils.GetFreePort(initPort + testItem.QueueNum); | ||||||
|         testItem.Port = port; |         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 |         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) |         if (result.Success != true) | ||||||
|         { |         { | ||||||
|             return result; |             return result; | ||||||
|  | @ -140,7 +123,7 @@ public class CoreConfigHandler | ||||||
|         { |         { | ||||||
|             result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds); |             result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds); | ||||||
|         } |         } | ||||||
|         else |         else if (coreType == ECoreType.Xray) | ||||||
|         { |         { | ||||||
|             result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad); |             result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad); | ||||||
|         } |         } | ||||||
|  | @ -152,4 +135,96 @@ public class CoreConfigHandler | ||||||
|         await File.WriteAllTextAsync(fileName, result.Data.ToString()); |         await File.WriteAllTextAsync(fileName, result.Data.ToString()); | ||||||
|         return result; |         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); | ||||||
|  |             case ECoreType.hysteria2: | ||||||
|  |                 return new CoreConfigHy2Service(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."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,8 @@ | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Text; | using System.Text; | ||||||
|  | using ServiceLib.Enums; | ||||||
|  | using ServiceLib.Models; | ||||||
|  | using static SQLite.SQLite3; | ||||||
| 
 | 
 | ||||||
| namespace ServiceLib.Handler; | namespace ServiceLib.Handler; | ||||||
| 
 | 
 | ||||||
|  | @ -25,8 +28,6 @@ public class CoreHandler | ||||||
|         Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); |         Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); | ||||||
|         Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); |         Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); | ||||||
|         Environment.SetEnvironmentVariable(Global.XrayLocalCert, 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) |         //Copy the bin folder to the storage location (for init) | ||||||
|         if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1") |         if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1") | ||||||
|  | @ -73,28 +74,23 @@ public class CoreHandler | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); |         // Create launch context and configure parameters | ||||||
|         var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); |         var context = new CoreLaunchContext(node, _config); | ||||||
|         if (result.Success != true) |         context.AdjustForConfigType(); | ||||||
|  | 
 | ||||||
|  |         // Start main core | ||||||
|  |         if (!await CoreStart(context)) | ||||||
|         { |         { | ||||||
|             UpdateFunc(true, result.Msg); |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         UpdateFunc(false, $"{node.GetSummary()}"); |         // Start pre-core if needed | ||||||
|         UpdateFunc(false, $"{Utils.GetRuntimeInfo()}"); |         if (!await CoreStartPreService(context)) | ||||||
|         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 CoreStop(); // Clean up main core if pre-core fails | ||||||
|             await WindowsUtils.RemoveTunDevice(); |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         await CoreStart(node); |  | ||||||
|         await CoreStartPreService(node); |  | ||||||
|         if (_process != null) |         if (_process != null) | ||||||
|         { |         { | ||||||
|             UpdateFunc(true, $"{node.GetSummary()}"); |             UpdateFunc(true, $"{node.GetSummary()}"); | ||||||
|  | @ -103,9 +99,9 @@ public class CoreHandler | ||||||
| 
 | 
 | ||||||
|     public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) |     public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> 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 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); |         var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); | ||||||
|         UpdateFunc(false, result.Msg); |         UpdateFunc(false, result.Msg); | ||||||
|         if (result.Success != true) |         if (result.Success != true) | ||||||
|  | @ -134,15 +130,17 @@ public class CoreHandler | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         var context = new CoreLaunchContext(node, _config); | ||||||
|  |         context.AdjustForConfigType(); | ||||||
|  |         var coreType = context.CoreType; | ||||||
|         var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); |         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); |         var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, context, testItem, configPath); | ||||||
|         if (result.Success != true) |         if (result.Success != true) | ||||||
|         { |         { | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); |  | ||||||
|         var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); |         var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); | ||||||
|         var proc = await RunProcess(coreInfo, fileName, true, false); |         var proc = await RunProcess(coreInfo, fileName, true, false); | ||||||
|         if (proc is null) |         if (proc is null) | ||||||
|  | @ -183,43 +181,84 @@ public class CoreHandler | ||||||
| 
 | 
 | ||||||
|     #region Private |     #region Private | ||||||
| 
 | 
 | ||||||
|     private async Task CoreStart(ProfileItem node) |     private async Task<bool> CoreStart(CoreLaunchContext context) | ||||||
|     { |     { | ||||||
|         var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); |         var coreType = context.SplitCore ? context.PureEndpointCore : context.CoreType; | ||||||
|         var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); |         var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName, coreType); | ||||||
|  |         var result = context.SplitCore | ||||||
|  |             ? await CoreConfigHandler.GeneratePassthroughConfig(context, fileName) | ||||||
|  |             : await CoreConfigHandler.GenerateClientConfig(context, fileName); | ||||||
| 
 | 
 | ||||||
|         var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; |         if (result.Success != true) | ||||||
|         var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, 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, Utils.GetBinConfigFileName(Global.CoreConfigFileName, coreType), displayLog, true); | ||||||
|  |          | ||||||
|         if (proc is null) |         if (proc is null) | ||||||
|         { |         { | ||||||
|             return; |             UpdateFunc(true, ResUI.FailedToRunCore); | ||||||
|  |             return false; | ||||||
|         } |         } | ||||||
|  |          | ||||||
|         _process = proc; |         _process = proc; | ||||||
|  |         _config.RunningCoreType = (ECoreType)(context.PreCoreType != null ? context.PreCoreType : coreType); | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async Task CoreStartPreService(ProfileItem node) |     private async Task<bool> CoreStartPreService(CoreLaunchContext context) | ||||||
|     { |     { | ||||||
|         if (_process != null && !_process.HasExited) |         if (context.PreCoreType == null) | ||||||
|         { |         { | ||||||
|             var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); |             return true; // No pre-core needed, consider successful | ||||||
|             var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType); |  | ||||||
|             if (itemSocks != null) |  | ||||||
|             { |  | ||||||
|                 var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box; |  | ||||||
|                 var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); |  | ||||||
|                 var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName); |  | ||||||
|                 if (result.Success) |  | ||||||
|                 { |  | ||||||
|                     var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); |  | ||||||
|                     var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); |  | ||||||
|                     if (proc is null) |  | ||||||
|                     { |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|                     _processPre = proc; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName, (ECoreType)context.PreCoreType); | ||||||
|  |         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 itemSocksLaunch = new CoreLaunchContext(itemSocks, _config); | ||||||
|  | 
 | ||||||
|  |         var result = await CoreConfigHandler.GenerateClientConfig(itemSocksLaunch, fileName); | ||||||
|  |         if (!result.Success) | ||||||
|  |         { | ||||||
|  |             UpdateFunc(true, result.Msg); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var coreInfo = CoreInfoHandler.Instance.GetCoreInfo((ECoreType)context.PreCoreType); | ||||||
|  |         var proc = await RunProcess(coreInfo, Utils.GetBinConfigFileName(Global.CorePreConfigFileName, (ECoreType)context.PreCoreType), 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) |     private void UpdateFunc(bool notify, string msg) | ||||||
|  | @ -269,7 +308,7 @@ public class CoreHandler | ||||||
|             StartInfo = new() |             StartInfo = new() | ||||||
|             { |             { | ||||||
|                 FileName = fileName, |                 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(), |                 WorkingDirectory = Utils.GetBinConfigPath(), | ||||||
|                 UseShellExecute = false, |                 UseShellExecute = false, | ||||||
|                 RedirectStandardOutput = displayLog, |                 RedirectStandardOutput = displayLog, | ||||||
|  |  | ||||||
|  | @ -130,7 +130,7 @@ public sealed class CoreInfoHandler | ||||||
|                 { |                 { | ||||||
|                     CoreType = ECoreType.hysteria, |                     CoreType = ECoreType.hysteria, | ||||||
|                     CoreExes = ["hysteria"], |                     CoreExes = ["hysteria"], | ||||||
|                     Arguments = "", |                     Arguments = "-c {0}", | ||||||
|                     Url = GetCoreUrl(ECoreType.hysteria), |                     Url = GetCoreUrl(ECoreType.hysteria), | ||||||
|                 }, |                 }, | ||||||
| 
 | 
 | ||||||
|  | @ -180,7 +180,7 @@ public sealed class CoreInfoHandler | ||||||
|                 { |                 { | ||||||
|                     CoreType = ECoreType.hysteria2, |                     CoreType = ECoreType.hysteria2, | ||||||
|                     CoreExes = ["hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria"], |                     CoreExes = ["hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria"], | ||||||
|                     Arguments = "", |                     Arguments = "-c {0}", | ||||||
|                     Url = GetCoreUrl(ECoreType.hysteria2), |                     Url = GetCoreUrl(ECoreType.hysteria2), | ||||||
|                 }, |                 }, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										49
									
								
								v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | 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); | ||||||
|  |         _ = 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<string, string>(); | ||||||
|  |         _ = GetStdTransport(item, Global.None, ref dicQuery); | ||||||
|  | 
 | ||||||
|  |         return ToUri(EConfigType.Anytls, item.Address, item.Port, pw, dicQuery, remark); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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<string, string>(); | ||||||
|  |         _ = GetStdTransport(item, Global.None, ref dicQuery); | ||||||
|  | 
 | ||||||
|  |         return ToUri(EConfigType.Brook, item.Address, item.Port, pw, dicQuery, remark); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -18,6 +18,11 @@ public class FmtHandler | ||||||
|                 EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item), |                 EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item), | ||||||
|                 EConfigType.TUIC => TuicFmt.ToUri(item), |                 EConfigType.TUIC => TuicFmt.ToUri(item), | ||||||
|                 EConfigType.WireGuard => WireguardFmt.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, |                 _ => null, | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|  | @ -75,6 +80,26 @@ public class FmtHandler | ||||||
|             { |             { | ||||||
|                 return WireguardFmt.Resolve(str, out msg); |                 return WireguardFmt.Resolve(str, out msg); | ||||||
|             } |             } | ||||||
|  |             else if (str.StartsWith(Global.ProtocolShares[EConfigType.Anytls])) | ||||||
|  |             { | ||||||
|  |                 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 |             else | ||||||
|             { |             { | ||||||
|                 msg = ResUI.NonvmessOrssProtocol; |                 msg = ResUI.NonvmessOrssProtocol; | ||||||
|  |  | ||||||
							
								
								
									
										63
									
								
								v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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<string, string>(); | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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<string, string>(); | ||||||
|  |         _ = GetStdTransport(item, Global.None, ref dicQuery); | ||||||
|  |         dicQuery.Add("protocol", item.HeaderType); | ||||||
|  | 
 | ||||||
|  |         return ToUri(EConfigType.NaiveProxy, item.Address, item.Port, pw, dicQuery, remark); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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<string, string>(); | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -48,6 +48,7 @@ public class Config | ||||||
|     public List<InItem> Inbound { get; set; } |     public List<InItem> Inbound { get; set; } | ||||||
|     public List<KeyEventItem> GlobalHotkeys { get; set; } |     public List<KeyEventItem> GlobalHotkeys { get; set; } | ||||||
|     public List<CoreTypeItem> CoreTypeItem { get; set; } |     public List<CoreTypeItem> CoreTypeItem { get; set; } | ||||||
|  |     public SplitCoreItem SplitCoreItem { get; set; } | ||||||
| 
 | 
 | ||||||
|     #endregion other entities |     #endregion other entities | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -138,6 +138,14 @@ public class CoreTypeItem | ||||||
|     public ECoreType CoreType { get; set; } |     public ECoreType CoreType { get; set; } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [Serializable] | ||||||
|  | public class SplitCoreItem | ||||||
|  | { | ||||||
|  |     public bool EnableSplitCore { get; set; } | ||||||
|  |     public List<CoreTypeItem> SplitCoreTypes { get; set; } | ||||||
|  |     public ECoreType RouteCoreType { get; set; } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| [Serializable] | [Serializable] | ||||||
| public class TunModeItem | public class TunModeItem | ||||||
| { | { | ||||||
|  |  | ||||||
							
								
								
									
										53
									
								
								v2rayN/ServiceLib/Models/CoreLaunchContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								v2rayN/ServiceLib/Models/CoreLaunchContext.cs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | ||||||
|  | namespace ServiceLib.Models; | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// Core launch context that encapsulates all parameters required for launching | ||||||
|  | /// </summary> | ||||||
|  | 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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Adjust context parameters based on configuration type | ||||||
|  |     /// </summary> | ||||||
|  |     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); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  | 
 | ||||||
| namespace ServiceLib.Models; | namespace ServiceLib.Models; | ||||||
| 
 | 
 | ||||||
| public class SingboxConfig | public class SingboxConfig | ||||||
|  | @ -6,6 +8,7 @@ public class SingboxConfig | ||||||
|     public Dns4Sbox? dns { get; set; } |     public Dns4Sbox? dns { get; set; } | ||||||
|     public List<Inbound4Sbox> inbounds { get; set; } |     public List<Inbound4Sbox> inbounds { get; set; } | ||||||
|     public List<Outbound4Sbox> outbounds { get; set; } |     public List<Outbound4Sbox> outbounds { get; set; } | ||||||
|  |     public List<Endpoints4Sbox>? endpoints { get; set; } | ||||||
|     public Route4Sbox route { get; set; } |     public Route4Sbox route { get; set; } | ||||||
|     public Experimental4Sbox? experimental { get; set; } |     public Experimental4Sbox? experimental { get; set; } | ||||||
| } | } | ||||||
|  | @ -29,7 +32,6 @@ public class Dns4Sbox | ||||||
|     public bool? independent_cache { get; set; } |     public bool? independent_cache { get; set; } | ||||||
|     public bool? reverse_mapping { get; set; } |     public bool? reverse_mapping { get; set; } | ||||||
|     public string? client_subnet { get; set; } |     public string? client_subnet { get; set; } | ||||||
|     public Fakeip4Sbox? fakeip { get; set; } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| public class Route4Sbox | public class Route4Sbox | ||||||
|  | @ -37,6 +39,7 @@ public class Route4Sbox | ||||||
|     public bool? auto_detect_interface { get; set; } |     public bool? auto_detect_interface { get; set; } | ||||||
|     public List<Rule4Sbox> rules { get; set; } |     public List<Rule4Sbox> rules { get; set; } | ||||||
|     public List<Ruleset4Sbox>? rule_set { get; set; } |     public List<Ruleset4Sbox>? rule_set { get; set; } | ||||||
|  |     public string? final { get; set; } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| [Serializable] | [Serializable] | ||||||
|  | @ -49,6 +52,7 @@ public class Rule4Sbox | ||||||
|     public string? mode { get; set; } |     public string? mode { get; set; } | ||||||
|     public bool? ip_is_private { get; set; } |     public bool? ip_is_private { get; set; } | ||||||
|     public string? client_subnet { get; set; } |     public string? client_subnet { get; set; } | ||||||
|  |     public int? rewrite_ttl { get; set; } | ||||||
|     public bool? invert { get; set; } |     public bool? invert { get; set; } | ||||||
|     public string? clash_mode { get; set; } |     public string? clash_mode { get; set; } | ||||||
|     public List<string>? inbound { get; set; } |     public List<string>? inbound { get; set; } | ||||||
|  | @ -67,6 +71,27 @@ public class Rule4Sbox | ||||||
|     public List<string>? process_name { get; set; } |     public List<string>? process_name { get; set; } | ||||||
|     public List<string>? rule_set { get; set; } |     public List<string>? rule_set { get; set; } | ||||||
|     public List<Rule4Sbox>? rules { get; set; } |     public List<Rule4Sbox>? rules { get; set; } | ||||||
|  |     public string? action { get; set; } | ||||||
|  |     public string? strategy { get; set; } | ||||||
|  |     public List<string>? sniffer { get; set; } | ||||||
|  |     public string? rcode { get; set; } | ||||||
|  |     public List<object>? query_type { get; set; } | ||||||
|  |     public List<string>? answer { get; set; } | ||||||
|  |     public List<string>? ns { get; set; } | ||||||
|  |     public List<string>? 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<string>? source_port_range { get; set; } | ||||||
|  |     public List<string>? network_type { get; set; } | ||||||
|  |     public bool? network_is_expensive { get; set; } | ||||||
|  |     public bool? network_is_constrained { get; set; } | ||||||
|  |     public List<string>? wifi_ssid { get; set; } | ||||||
|  |     public List<string>? 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] | [Serializable] | ||||||
|  | @ -76,7 +101,6 @@ public class Inbound4Sbox | ||||||
|     public string tag { get; set; } |     public string tag { get; set; } | ||||||
|     public string listen { get; set; } |     public string listen { get; set; } | ||||||
|     public int? listen_port { get; set; } |     public int? listen_port { get; set; } | ||||||
|     public string? domain_strategy { get; set; } |  | ||||||
|     public string interface_name { get; set; } |     public string interface_name { get; set; } | ||||||
|     public List<string>? address { get; set; } |     public List<string>? address { get; set; } | ||||||
|     public int? mtu { get; set; } |     public int? mtu { get; set; } | ||||||
|  | @ -84,8 +108,6 @@ public class Inbound4Sbox | ||||||
|     public bool? strict_route { get; set; } |     public bool? strict_route { get; set; } | ||||||
|     public bool? endpoint_independent_nat { get; set; } |     public bool? endpoint_independent_nat { get; set; } | ||||||
|     public string? stack { get; set; } |     public string? stack { get; set; } | ||||||
|     public bool? sniff { get; set; } |  | ||||||
|     public bool? sniff_override_destination { get; set; } |  | ||||||
|     public List<User4Sbox> users { get; set; } |     public List<User4Sbox> users { get; set; } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -95,10 +117,8 @@ public class User4Sbox | ||||||
|     public string password { get; set; } |     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 string? server { get; set; } | ||||||
|     public int? server_port { get; set; } |     public int? server_port { get; set; } | ||||||
|     public List<string>? server_ports { get; set; } |     public List<string>? server_ports { get; set; } | ||||||
|  | @ -113,7 +133,6 @@ public class Outbound4Sbox | ||||||
|     public int? recv_window_conn { get; set; } |     public int? recv_window_conn { get; set; } | ||||||
|     public int? recv_window { get; set; } |     public int? recv_window { get; set; } | ||||||
|     public bool? disable_mtu_discovery { get; set; } |     public bool? disable_mtu_discovery { get; set; } | ||||||
|     public string? detour { get; set; } |  | ||||||
|     public string? method { get; set; } |     public string? method { get; set; } | ||||||
|     public string? username { get; set; } |     public string? username { get; set; } | ||||||
|     public string? password { get; set; } |     public string? password { get; set; } | ||||||
|  | @ -121,21 +140,36 @@ public class Outbound4Sbox | ||||||
|     public string? version { get; set; } |     public string? version { get; set; } | ||||||
|     public string? network { get; set; } |     public string? network { get; set; } | ||||||
|     public string? packet_encoding { get; set; } |     public string? packet_encoding { get; set; } | ||||||
|     public List<string>? local_address { get; set; } |  | ||||||
|     public string? private_key { get; set; } |  | ||||||
|     public string? peer_public_key { get; set; } |  | ||||||
|     public List<int>? reserved { get; set; } |  | ||||||
|     public int? mtu { get; set; } |  | ||||||
|     public string? plugin { get; set; } |     public string? plugin { get; set; } | ||||||
|     public string? plugin_opts { 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<string>? outbounds { get; set; } |     public List<string>? outbounds { get; set; } | ||||||
|     public bool? interrupt_exist_connections { get; set; } |     public bool? interrupt_exist_connections { get; set; } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | public class Endpoints4Sbox : BaseServer4Sbox | ||||||
|  | { | ||||||
|  |     public bool? system { get; set; } | ||||||
|  |     public string? name { get; set; } | ||||||
|  |     public int? mtu { get; set; } | ||||||
|  |     public List<string> 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<Peer4Sbox> 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<string> allowed_ips { get; set; } | ||||||
|  |     public int? persistent_keepalive_interval { get; set; } | ||||||
|  |     public List<int> reserved { get; set; } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| public class Tls4Sbox | public class Tls4Sbox | ||||||
| { | { | ||||||
|     public bool enabled { get; set; } |     public bool enabled { get; set; } | ||||||
|  | @ -191,15 +225,25 @@ public class HyObfs4Sbox | ||||||
|     public string? password { get; set; } |     public string? password { get; set; } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| public class Server4Sbox | public class Server4Sbox : BaseServer4Sbox | ||||||
| { | { | ||||||
|     public string? tag { get; set; } |     public string? inet4_range { get; set; } | ||||||
|  |     public string? inet6_range { get; set; } | ||||||
|  |     public string? client_subnet { get; set; } | ||||||
|  |     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; } | ||||||
|  |     // public List<string>? path { get; set; } // hosts | ||||||
|  |     public Dictionary<string, object>? predefined { get; set; } | ||||||
|  |     // Deprecated | ||||||
|     public string? address { get; set; } |     public string? address { get; set; } | ||||||
|     public string? address_resolver { get; set; } |     public string? address_resolver { get; set; } | ||||||
|     public string? address_strategy { get; set; } |     public string? address_strategy { get; set; } | ||||||
|     public string? strategy { get; set; } |     public string? strategy { get; set; } | ||||||
|     public string? detour { get; set; } |     // Deprecated End | ||||||
|     public string? client_subnet { get; set; } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| public class Experimental4Sbox | public class Experimental4Sbox | ||||||
|  | @ -229,13 +273,6 @@ public class Stats4Sbox | ||||||
|     public List<string>? users { get; set; } |     public List<string>? 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 class CacheFile4Sbox | ||||||
| { | { | ||||||
|     public bool enabled { get; set; } |     public bool enabled { get; set; } | ||||||
|  | @ -254,3 +291,33 @@ public class Ruleset4Sbox | ||||||
|     public string? download_detour { get; set; } |     public string? download_detour { get; set; } | ||||||
|     public string? update_interval { 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<string>? network_type { get; set; } | ||||||
|  |     public List<string>? 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; } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										81
									
								
								v2rayN/ServiceLib/Resx/ResUI.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										81
									
								
								v2rayN/ServiceLib/Resx/ResUI.Designer.cs
									
									
									
										generated
									
									
									
								
							|  | @ -654,6 +654,24 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Add [Anytls] Configuration 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string menuAddAnytlsServer { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("menuAddAnytlsServer", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Add [Brook] Configuration 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string menuAddBrookServer { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("menuAddBrookServer", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Add a custom configuration Configuration 的本地化字符串。 |         ///   查找类似 Add a custom configuration Configuration 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -681,6 +699,24 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Add [Juicity] Configuration 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string menuAddJuicityServer { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("menuAddJuicityServer", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Add [Naive] Configuration 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string menuAddNaiveServer { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("menuAddNaiveServer", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。 |         ///   查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -708,6 +744,15 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Add [Shadowquic] Configuration 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string menuAddShadowquicServer { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("menuAddShadowquicServer", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Add [Shadowsocks] Configuration 的本地化字符串。 |         ///   查找类似 Add [Shadowsocks] Configuration 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -2463,6 +2508,15 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Proxy Protocol 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbHeaderType100 { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbHeaderType100", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 Congestion control 的本地化字符串。 |         ///   查找类似 Congestion control 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | @ -3417,6 +3471,33 @@ namespace ServiceLib.Resx { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbSettingsSplitCoreDoc1 { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbSettingsSplitCoreDoc1", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Routing Core defaults to sing-box when Tun is enabled. 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbSettingsSplitCoreDoc2 { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbSettingsSplitCoreDoc2", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   查找类似 Enable separation of outbound and routing cores 的本地化字符串。 | ||||||
|  |         /// </summary> | ||||||
|  |         public static string TbSettingsSplitCoreEnable { | ||||||
|  |             get { | ||||||
|  |                 return ResourceManager.GetString("TbSettingsSplitCoreEnable", resourceCulture); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   查找类似 sing-box ruleset files source (optional) 的本地化字符串。 |         ///   查找类似 sing-box ruleset files source (optional) 的本地化字符串。 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  |  | ||||||
|  | @ -1401,4 +1401,31 @@ | ||||||
|   <data name="TbMldsa65Verify" xml:space="preserve"> |   <data name="TbMldsa65Verify" xml:space="preserve"> | ||||||
|     <value>Mldsa65Verify</value> |     <value>Mldsa65Verify</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="menuAddAnytlsServer" xml:space="preserve"> | ||||||
|  |     <value>Add [Anytls] Configuration</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreEnable" xml:space="preserve"> | ||||||
|  |     <value>Enable separation of outbound and routing cores</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc1" xml:space="preserve"> | ||||||
|  |     <value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc2" xml:space="preserve"> | ||||||
|  |     <value>Routing Core defaults to sing-box when Tun is enabled.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddBrookServer" xml:space="preserve"> | ||||||
|  |     <value>افزودن سرور [Brook]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddJuicityServer" xml:space="preserve"> | ||||||
|  |     <value>افزودن سرور [Juicity]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddNaiveServer" xml:space="preserve"> | ||||||
|  |     <value>افزودن سرور [Naive]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddShadowquicServer" xml:space="preserve"> | ||||||
|  |     <value>افزودن سرور [Shadowquic]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbHeaderType100" xml:space="preserve"> | ||||||
|  |     <value>Proxy Protocol</value> | ||||||
|  |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1401,4 +1401,31 @@ | ||||||
|   <data name="TbMldsa65Verify" xml:space="preserve"> |   <data name="TbMldsa65Verify" xml:space="preserve"> | ||||||
|     <value>Mldsa65Verify</value> |     <value>Mldsa65Verify</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="menuAddAnytlsServer" xml:space="preserve"> | ||||||
|  |     <value>[Anytls] konfiguráció hozzáadása</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreEnable" xml:space="preserve"> | ||||||
|  |     <value>Enable separation of outbound and routing cores</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc1" xml:space="preserve"> | ||||||
|  |     <value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc2" xml:space="preserve"> | ||||||
|  |     <value>Routing Core defaults to sing-box when Tun is enabled.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddBrookServer" xml:space="preserve"> | ||||||
|  |     <value>[Brook] szerver hozzáadása</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddJuicityServer" xml:space="preserve"> | ||||||
|  |     <value>[Juicity] szerver hozzáadása</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddNaiveServer" xml:space="preserve"> | ||||||
|  |     <value>[Naive] szerver hozzáadása</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddShadowquicServer" xml:space="preserve"> | ||||||
|  |     <value>[Shadowquic] szerver hozzáadása</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbHeaderType100" xml:space="preserve"> | ||||||
|  |     <value>Proxy Protocol</value> | ||||||
|  |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1401,4 +1401,31 @@ | ||||||
|   <data name="TbMldsa65Verify" xml:space="preserve"> |   <data name="TbMldsa65Verify" xml:space="preserve"> | ||||||
|     <value>Mldsa65Verify</value> |     <value>Mldsa65Verify</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="menuAddAnytlsServer" xml:space="preserve"> | ||||||
|  |     <value>Add [Anytls] Configuration</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreEnable" xml:space="preserve"> | ||||||
|  |     <value>Enable separation of outbound and routing cores</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc1" xml:space="preserve"> | ||||||
|  |     <value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc2" xml:space="preserve"> | ||||||
|  |     <value>Routing Core defaults to sing-box when Tun is enabled.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddBrookServer" xml:space="preserve"> | ||||||
|  |     <value>Add [Brook] Configuration</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddJuicityServer" xml:space="preserve"> | ||||||
|  |     <value>Add [Juicity] Configuration</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddNaiveServer" xml:space="preserve"> | ||||||
|  |     <value>Add [Naive] Configuration</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddShadowquicServer" xml:space="preserve"> | ||||||
|  |     <value>Add [Shadowquic] Configuration</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbHeaderType100" xml:space="preserve"> | ||||||
|  |     <value>Proxy Protocol</value> | ||||||
|  |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1401,4 +1401,31 @@ | ||||||
|   <data name="TbMldsa65Verify" xml:space="preserve"> |   <data name="TbMldsa65Verify" xml:space="preserve"> | ||||||
|     <value>Mldsa65Verify</value> |     <value>Mldsa65Verify</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="menuAddAnytlsServer" xml:space="preserve"> | ||||||
|  |     <value>Добавить сервер [Anytls]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreEnable" xml:space="preserve"> | ||||||
|  |     <value>Enable separation of outbound and routing cores</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc1" xml:space="preserve"> | ||||||
|  |     <value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc2" xml:space="preserve"> | ||||||
|  |     <value>Routing Core defaults to sing-box when Tun is enabled.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddBrookServer" xml:space="preserve"> | ||||||
|  |     <value>Добавить сервер [Brook]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddJuicityServer" xml:space="preserve"> | ||||||
|  |     <value>Добавить сервер [Juicity]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddNaiveServer" xml:space="preserve"> | ||||||
|  |     <value>Добавить сервер [Naive]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddShadowquicServer" xml:space="preserve"> | ||||||
|  |     <value>Добавить сервер [Shadowquic]</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbHeaderType100" xml:space="preserve"> | ||||||
|  |     <value>Proxy Protocol</value> | ||||||
|  |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1398,4 +1398,31 @@ | ||||||
|   <data name="TbMldsa65Verify" xml:space="preserve"> |   <data name="TbMldsa65Verify" xml:space="preserve"> | ||||||
|     <value>Mldsa65Verify</value> |     <value>Mldsa65Verify</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="menuAddAnytlsServer" xml:space="preserve"> | ||||||
|  |     <value>添加 [Anytls] 配置文件</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreEnable" xml:space="preserve"> | ||||||
|  |     <value>启用出站核心与路由核心分离</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc1" xml:space="preserve"> | ||||||
|  |     <value>出站与路由分离,出站与分流 Core 类型不同时,将启用两个核心</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc2" xml:space="preserve"> | ||||||
|  |     <value>启用 Tun 时,路由 Core 为 sing-box</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddBrookServer" xml:space="preserve"> | ||||||
|  |     <value>添加 [Brook] 配置文件</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddJuicityServer" xml:space="preserve"> | ||||||
|  |     <value>添加 [Juicity] 配置文件</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddNaiveServer" xml:space="preserve"> | ||||||
|  |     <value>添加 [Naive] 配置文件</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddShadowquicServer" xml:space="preserve"> | ||||||
|  |     <value>添加 [Shadowquic] 配置文件</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbHeaderType100" xml:space="preserve"> | ||||||
|  |     <value>代理协议</value> | ||||||
|  |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1398,4 +1398,31 @@ | ||||||
|   <data name="TbMldsa65Verify" xml:space="preserve"> |   <data name="TbMldsa65Verify" xml:space="preserve"> | ||||||
|     <value>Mldsa65Verify</value> |     <value>Mldsa65Verify</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="menuAddAnytlsServer" xml:space="preserve"> | ||||||
|  |     <value>新增 [Anytls] 設定檔</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreEnable" xml:space="preserve"> | ||||||
|  |     <value>Enable separation of outbound and routing cores</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc1" xml:space="preserve"> | ||||||
|  |     <value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbSettingsSplitCoreDoc2" xml:space="preserve"> | ||||||
|  |     <value>Routing Core defaults to sing-box when Tun is enabled.</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddBrookServer" xml:space="preserve"> | ||||||
|  |     <value>新增 [Brook] 設定檔</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddJuicityServer" xml:space="preserve"> | ||||||
|  |     <value>新增 [Juicity] 設定檔</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddNaiveServer" xml:space="preserve"> | ||||||
|  |     <value>新增 [Naive] 設定檔</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="menuAddShadowquicServer" xml:space="preserve"> | ||||||
|  |     <value>新增 [Shadowquic] 設定檔</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="TbHeaderType100" xml:space="preserve"> | ||||||
|  |     <value>Proxy Protocol</value> | ||||||
|  |   </data> | ||||||
| </root> | </root> | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| { | { | ||||||
| 	"log": { | 	"log": { | ||||||
| 		"level": "debug", | 		"level": "debug", | ||||||
| 		"timestamp": true | 		"timestamp": true | ||||||
|  | @ -14,22 +14,10 @@ | ||||||
| 		{ | 		{ | ||||||
| 			"type": "direct", | 			"type": "direct", | ||||||
| 			"tag": "direct" | 			"tag": "direct" | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			"type": "block", |  | ||||||
| 			"tag": "block" |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			"tag": "dns_out", |  | ||||||
| 			"type": "dns" |  | ||||||
| 		} | 		} | ||||||
| 	], | 	], | ||||||
| 	"route": { | 	"route": { | ||||||
| 		"rules": [ | 		"rules": [ | ||||||
| 			{ |  | ||||||
| 				"protocol": [ "dns" ], |  | ||||||
| 				"outbound": "dns_out" |  | ||||||
| 			} |  | ||||||
| 		] | 		] | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -2,28 +2,33 @@ | ||||||
|   "servers": [ |   "servers": [ | ||||||
|     { |     { | ||||||
|       "tag": "remote", |       "tag": "remote", | ||||||
|       "address": "tcp://8.8.8.8", |       "type": "tcp", | ||||||
|       "strategy": "prefer_ipv4", |       "server": "8.8.8.8", | ||||||
|       "detour": "proxy" |       "detour": "proxy" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "tag": "local", |       "tag": "local", | ||||||
|       "address": "223.5.5.5", |       "type": "udp", | ||||||
|       "strategy": "prefer_ipv4", |       "server": "223.5.5.5" | ||||||
|       "detour": "direct" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "tag": "block", |  | ||||||
|       "address": "rcode://success" |  | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "rules": [ |   "rules": [ | ||||||
|  |     { | ||||||
|  |       "domain_suffix": [ | ||||||
|  |         "googleapis.cn", | ||||||
|  |         "gstatic.com" | ||||||
|  |       ], | ||||||
|  |       "server": "remote", | ||||||
|  |       "strategy": "prefer_ipv4" | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       "rule_set": [ |       "rule_set": [ | ||||||
|         "geosite-cn" |         "geosite-cn" | ||||||
|       ], |       ], | ||||||
|       "server": "local" |       "server": "local", | ||||||
|  |       "strategy": "prefer_ipv4" | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "final": "remote" |   "final": "remote", | ||||||
|  |   "strategy": "prefer_ipv4" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,29 +2,33 @@ | ||||||
|   "servers": [ |   "servers": [ | ||||||
|     { |     { | ||||||
|       "tag": "remote", |       "tag": "remote", | ||||||
|       "address": "tcp://8.8.8.8", |       "type": "tcp", | ||||||
|       "strategy": "prefer_ipv4", |       "server": "8.8.8.8", | ||||||
|       "detour": "proxy" |       "detour": "proxy" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "tag": "local", |       "tag": "local", | ||||||
|       "address": "223.5.5.5", |       "type": "udp", | ||||||
|       "strategy": "prefer_ipv4", |       "server": "223.5.5.5" | ||||||
|       "detour": "direct" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "tag": "block", |  | ||||||
|       "address": "rcode://success" |  | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "rules": [ |   "rules": [ | ||||||
|     { |     { | ||||||
|       "rule_set": [ |       "domain_suffix": [ | ||||||
|         "geosite-cn", |         "googleapis.cn", | ||||||
|         "geosite-geolocation-cn" |         "gstatic.com" | ||||||
|       ], |       ], | ||||||
|       "server": "local" |       "server": "remote", | ||||||
|  |       "strategy": "prefer_ipv4" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "rule_set": [ | ||||||
|  |         "geosite-cn" | ||||||
|  |       ], | ||||||
|  |       "server": "local", | ||||||
|  |       "strategy": "prefer_ipv4" | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "final": "remote" |   "final": "remote", | ||||||
| } |   "strategy": "prefer_ipv4" | ||||||
|  | } | ||||||
|  | @ -8,13 +8,13 @@ | ||||||
|       139, |       139, | ||||||
|       5353 |       5353 | ||||||
|     ], |     ], | ||||||
|     "outbound": "block" |     "action": "reject" | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     "ip_cidr": [ |     "ip_cidr": [ | ||||||
|       "224.0.0.0/3", |       "224.0.0.0/3", | ||||||
|       "ff00::/8" |       "ff00::/8" | ||||||
|     ], |     ], | ||||||
|     "outbound": "block" |     "action": "reject" | ||||||
|   } |   } | ||||||
| ] | ] | ||||||
|  | @ -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<RetResult> GeneratePassthroughConfig(ProfileItem node) | ||||||
|  |     { | ||||||
|  |         return GeneratePassthroughConfig(node, AppHandler.Instance.GetLocalPort(EInboundProtocol.split)); | ||||||
|  |     } | ||||||
|  |     public virtual Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         return GeneratePassthroughConfig(node, port); | ||||||
|  |     } | ||||||
|  |     protected abstract Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port); | ||||||
|  |     public virtual async Task<RetResult> 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<RetResult> GenerateClientConfigContent(ProfileItem node); | ||||||
|  |     public abstract Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad); | ||||||
|  |     public virtual Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds) | ||||||
|  |     { | ||||||
|  |         return GenerateClientMultipleLoadConfig(selecteds, EMultipleLoad.LeastPing); | ||||||
|  |     } | ||||||
|  |     public abstract Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds); | ||||||
|  | } | ||||||
|  | @ -1,22 +1,19 @@ | ||||||
| using System.Data; | using System.Data; | ||||||
|  | using System.Linq; | ||||||
| using System.Net; | using System.Net; | ||||||
| using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||||
|  | using System.Reactive; | ||||||
|  | using System.Text.Json.Nodes; | ||||||
|  | using DynamicData; | ||||||
|  | using ServiceLib.Models; | ||||||
| 
 | 
 | ||||||
| namespace ServiceLib.Services.CoreConfig; | 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 |     #region public gen function | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientConfigContent(ProfileItem node) |     public override async Task<RetResult> GenerateClientConfigContent(ProfileItem node) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         try |         try | ||||||
|  | @ -53,7 +50,18 @@ public class CoreConfigSingboxService | ||||||
| 
 | 
 | ||||||
|             await GenInbounds(singboxConfig); |             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); |             await GenMoreOutbounds(node, singboxConfig); | ||||||
| 
 | 
 | ||||||
|  | @ -78,7 +86,7 @@ public class CoreConfigSingboxService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds) |     public override async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         try |         try | ||||||
|  | @ -127,7 +135,7 @@ public class CoreConfigSingboxService | ||||||
| 
 | 
 | ||||||
|             foreach (var it in selecteds) |             foreach (var it in selecteds) | ||||||
|             { |             { | ||||||
|                 if (it.ConfigType == EConfigType.Custom) |                 if (!Global.SingboxSupportConfigType.Contains(it.ConfigType)) | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  | @ -202,16 +210,29 @@ public class CoreConfigSingboxService | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); |                 var server = await GenServer(item); | ||||||
|                 await GenOutbound(item, outbound); |                 if (server is null) | ||||||
|                 outbound.tag = Global.ProxyTag + inbound.listen_port.ToString(); |                 { | ||||||
|                 singboxConfig.outbounds.Add(outbound); |                     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 |                 //rule | ||||||
|                 Rule4Sbox rule = new() |                 Rule4Sbox rule = new() | ||||||
|                 { |                 { | ||||||
|                     inbound = new List<string> { inbound.tag }, |                     inbound = new List<string> { inbound.tag }, | ||||||
|                     outbound = outbound.tag |                     outbound = tag | ||||||
|                 }; |                 }; | ||||||
|                 singboxConfig.route.rules.Add(rule); |                 singboxConfig.route.rules.Add(rule); | ||||||
|             } |             } | ||||||
|  | @ -242,7 +263,7 @@ public class CoreConfigSingboxService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port) |     public override async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         try |         try | ||||||
|  | @ -275,7 +296,18 @@ public class CoreConfigSingboxService | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             await GenLog(singboxConfig); |             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 GenMoreOutbounds(node, singboxConfig); | ||||||
|             await GenDnsDomains(null, singboxConfig, null); |             await GenDnsDomains(null, singboxConfig, null); | ||||||
| 
 | 
 | ||||||
|  | @ -302,7 +334,7 @@ public class CoreConfigSingboxService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds) |     public override async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         try |         try | ||||||
|  | @ -339,7 +371,7 @@ public class CoreConfigSingboxService | ||||||
|             var proxyProfiles = new List<ProfileItem>(); |             var proxyProfiles = new List<ProfileItem>(); | ||||||
|             foreach (var it in selecteds) |             foreach (var it in selecteds) | ||||||
|             { |             { | ||||||
|                 if (it.ConfigType == EConfigType.Custom) |                 if (!Global.SingboxSupportConfigType.Contains(it.ConfigType)) | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  | @ -394,7 +426,7 @@ public class CoreConfigSingboxService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName) |     public override async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         if (node == null || fileName is null) |         if (node == null || fileName is null) | ||||||
|  | @ -475,6 +507,92 @@ public class CoreConfigSingboxService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         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<SingboxConfig>(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 = port | ||||||
|  |             }; | ||||||
|  |             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 = JsonUtils.Serialize(config, true); | ||||||
|  | 
 | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |             ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #endregion public gen function |     #endregion public gen function | ||||||
| 
 | 
 | ||||||
|     #region private gen function |     #region private gen function | ||||||
|  | @ -534,15 +652,6 @@ public class CoreConfigSingboxService | ||||||
|                 singboxConfig.inbounds.Add(inbound); |                 singboxConfig.inbounds.Add(inbound); | ||||||
| 
 | 
 | ||||||
|                 inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); |                 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) |                 if (_config.Inbound.First().SecondLocalPortEnabled) | ||||||
|                 { |                 { | ||||||
|  | @ -587,8 +696,6 @@ public class CoreConfigSingboxService | ||||||
|                 tunInbound.mtu = _config.TunModeItem.Mtu; |                 tunInbound.mtu = _config.TunModeItem.Mtu; | ||||||
|                 tunInbound.strict_route = _config.TunModeItem.StrictRoute; |                 tunInbound.strict_route = _config.TunModeItem.StrictRoute; | ||||||
|                 tunInbound.stack = _config.TunModeItem.Stack; |                 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) |                 if (_config.TunModeItem.EnableIPv6Address == false) | ||||||
|                 { |                 { | ||||||
|                     tunInbound.address = ["172.18.0.1/30"]; |                     tunInbound.address = ["172.18.0.1/30"]; | ||||||
|  | @ -621,6 +728,17 @@ public class CoreConfigSingboxService | ||||||
|             outbound.server_port = node.Port; |             outbound.server_port = node.Port; | ||||||
|             outbound.type = Global.ProtocolTypes[node.ConfigType]; |             outbound.type = Global.ProtocolTypes[node.ConfigType]; | ||||||
| 
 | 
 | ||||||
|  |             if (Utils.IsDomain(node.Address)) | ||||||
|  |             { | ||||||
|  |                 var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); | ||||||
|  |                 var localDnsAddress = string.IsNullOrEmpty(item?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : item?.DomainDNSAddress; | ||||||
|  |                 outbound.domain_resolver = new() | ||||||
|  |                 { | ||||||
|  |                     server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver", | ||||||
|  |                     strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             switch (node.ConfigType) |             switch (node.ConfigType) | ||||||
|             { |             { | ||||||
|                 case EConfigType.VMess: |                 case EConfigType.VMess: | ||||||
|  | @ -730,13 +848,9 @@ public class CoreConfigSingboxService | ||||||
|                         outbound.congestion_control = node.HeaderType; |                         outbound.congestion_control = node.HeaderType; | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                 case EConfigType.WireGuard: |                 case EConfigType.Anytls: | ||||||
|                     { |                     { | ||||||
|                         outbound.private_key = node.Id; |                         outbound.password = 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; |                         break; | ||||||
|                     } |                     } | ||||||
|             } |             } | ||||||
|  | @ -752,6 +866,76 @@ public class CoreConfigSingboxService | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private async Task<int> GenEndpoint(ProfileItem node, Endpoints4Sbox endpoint) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             endpoint.address = Utils.String2List(node.RequestHost); | ||||||
|  |             endpoint.type = Global.ProtocolTypes[node.ConfigType]; | ||||||
|  | 
 | ||||||
|  |             if (Utils.IsDomain(node.Address)) | ||||||
|  |             { | ||||||
|  |                 var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); | ||||||
|  |                 var localDnsAddress = string.IsNullOrEmpty(item?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : item?.DomainDNSAddress; | ||||||
|  |                 endpoint.domain_resolver = new() | ||||||
|  |                 { | ||||||
|  |                     server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver", | ||||||
|  |                     strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch (node.ConfigType) | ||||||
|  |             { | ||||||
|  |                 case EConfigType.WireGuard: | ||||||
|  |                     { | ||||||
|  |                         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(); | ||||||
|  |                         endpoint.peers = new() { peer }; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |         } | ||||||
|  |         return await Task.FromResult(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private async Task<BaseServer4Sbox?> GenServer(ProfileItem node) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); | ||||||
|  |             if (node.ConfigType == EConfigType.WireGuard) | ||||||
|  |             { | ||||||
|  |                 var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound); | ||||||
|  |                 await GenEndpoint(node, endpoint); | ||||||
|  |                 return endpoint; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); | ||||||
|  |                 await GenOutbound(node, outbound); | ||||||
|  |                 return outbound; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |         } | ||||||
|  |         return await Task.FromResult<BaseServer4Sbox?>(null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) |     private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|  | @ -918,26 +1102,42 @@ public class CoreConfigSingboxService | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             //current proxy |             //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); |             var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); | ||||||
| 
 | 
 | ||||||
|             //Previous proxy |             //Previous proxy | ||||||
|             var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); |             var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); | ||||||
|             string? prevOutboundTag = null; |             string? prevOutboundTag = null; | ||||||
|             if (prevNode is not null |             if (prevNode is not null | ||||||
|                 && prevNode.ConfigType != EConfigType.Custom) |                 && Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)) | ||||||
|             { |             { | ||||||
|                 var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); |  | ||||||
|                 await GenOutbound(prevNode, prevOutbound); |  | ||||||
|                 prevOutboundTag = $"prev-{Global.ProxyTag}"; |                 prevOutboundTag = $"prev-{Global.ProxyTag}"; | ||||||
|                 prevOutbound.tag = prevOutboundTag; |                 var prevServer = await GenServer(prevNode); | ||||||
|                 singboxConfig.outbounds.Add(prevOutbound); |                 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) |         catch (Exception ex) | ||||||
|  | @ -960,11 +1160,13 @@ public class CoreConfigSingboxService | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var resultOutbounds = new List<Outbound4Sbox>(); |             var resultOutbounds = new List<Outbound4Sbox>(); | ||||||
|  |             var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints | ||||||
|             var prevOutbounds = new List<Outbound4Sbox>(); // Separate list for prev outbounds |             var prevOutbounds = new List<Outbound4Sbox>(); // Separate list for prev outbounds | ||||||
|  |             var prevEndpoints = new List<Endpoints4Sbox>(); // Separate list for prev endpoints | ||||||
|             var proxyTags = new List<string>(); // For selector and urltest outbounds |             var proxyTags = new List<string>(); // For selector and urltest outbounds | ||||||
| 
 | 
 | ||||||
|             // Cache for chain proxies to avoid duplicate generation |             // Cache for chain proxies to avoid duplicate generation | ||||||
|             var nextProxyCache = new Dictionary<string, Outbound4Sbox?>(); |             var nextProxyCache = new Dictionary<string, BaseServer4Sbox?>(); | ||||||
|             var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag |             var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag | ||||||
|             int prevIndex = 0; // Index for prev outbounds |             int prevIndex = 0; // Index for prev outbounds | ||||||
| 
 | 
 | ||||||
|  | @ -976,19 +1178,18 @@ public class CoreConfigSingboxService | ||||||
| 
 | 
 | ||||||
|                 // Handle proxy chain |                 // Handle proxy chain | ||||||
|                 string? prevTag = null; |                 string? prevTag = null; | ||||||
|                 var currentOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); |                 var currentServer = await GenServer(node); | ||||||
|                 var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null); |                 var nextServer = nextProxyCache.GetValueOrDefault(node.Subid, null); | ||||||
|                 if (nextOutbound != null) |                 if (nextServer != null) | ||||||
|                 { |                 { | ||||||
|                     nextOutbound = JsonUtils.DeepCopy(nextOutbound); |                     nextServer = JsonUtils.DeepCopy(nextServer); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 var subItem = await AppHandler.Instance.GetSubItem(node.Subid); |                 var subItem = await AppHandler.Instance.GetSubItem(node.Subid); | ||||||
| 
 | 
 | ||||||
|                 // current proxy |                 // current proxy | ||||||
|                 await GenOutbound(node, currentOutbound); |                 currentServer.tag = $"{Global.ProxyTag}-{index}"; | ||||||
|                 currentOutbound.tag = $"{Global.ProxyTag}-{index}"; |                 proxyTags.Add(currentServer.tag); | ||||||
|                 proxyTags.Add(currentOutbound.tag); |  | ||||||
| 
 | 
 | ||||||
|                 if (!node.Subid.IsNullOrEmpty()) |                 if (!node.Subid.IsNullOrEmpty()) | ||||||
|                 { |                 { | ||||||
|  | @ -1000,7 +1201,7 @@ public class CoreConfigSingboxService | ||||||
|                     { |                     { | ||||||
|                         var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); |                         var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); | ||||||
|                         if (prevNode is not null |                         if (prevNode is not null | ||||||
|                             && prevNode.ConfigType != EConfigType.Custom) |                             && Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)) | ||||||
|                         { |                         { | ||||||
|                             var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); |                             var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); | ||||||
|                             await GenOutbound(prevNode, prevOutbound); |                             await GenOutbound(prevNode, prevOutbound); | ||||||
|  | @ -1011,18 +1212,32 @@ public class CoreConfigSingboxService | ||||||
|                         prevProxyTags[node.Subid] = prevTag; |                         prevProxyTags[node.Subid] = prevTag; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound); |                     nextServer = await GenChainOutbounds(subItem, currentServer, prevTag, nextServer); | ||||||
|                     if (!nextProxyCache.ContainsKey(node.Subid)) |                     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) |             // Add urltest outbound (auto selection based on latency) | ||||||
|  | @ -1055,6 +1270,9 @@ public class CoreConfigSingboxService | ||||||
|             resultOutbounds.AddRange(prevOutbounds); |             resultOutbounds.AddRange(prevOutbounds); | ||||||
|             resultOutbounds.AddRange(singboxConfig.outbounds); |             resultOutbounds.AddRange(singboxConfig.outbounds); | ||||||
|             singboxConfig.outbounds = resultOutbounds; |             singboxConfig.outbounds = resultOutbounds; | ||||||
|  |             singboxConfig.endpoints ??= new List<Endpoints4Sbox>(); | ||||||
|  |             resultEndpoints.AddRange(singboxConfig.endpoints); | ||||||
|  |             singboxConfig.endpoints = resultEndpoints; | ||||||
|         } |         } | ||||||
|         catch (Exception ex) |         catch (Exception ex) | ||||||
|         { |         { | ||||||
|  | @ -1076,7 +1294,7 @@ public class CoreConfigSingboxService | ||||||
|     /// <returns> |     /// <returns> | ||||||
|     /// The outbound configuration for the next proxy in the chain, or null if no next proxy exists. |     /// The outbound configuration for the next proxy in the chain, or null if no next proxy exists. | ||||||
|     /// </returns> |     /// </returns> | ||||||
|     private async Task<Outbound4Sbox?> GenChainOutbounds(SubItem subItem, Outbound4Sbox outbound, string? prevOutboundTag, Outbound4Sbox? nextOutbound = null) |     private async Task<BaseServer4Sbox?> GenChainOutbounds(SubItem subItem, BaseServer4Sbox outbound, string? prevOutboundTag, BaseServer4Sbox? nextOutbound = null) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|  | @ -1090,13 +1308,9 @@ public class CoreConfigSingboxService | ||||||
|             // Next proxy |             // Next proxy | ||||||
|             var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); |             var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); | ||||||
|             if (nextNode is not null |             if (nextNode is not null | ||||||
|                 && nextNode.ConfigType != EConfigType.Custom) |                 && Global.SingboxSupportConfigType.Contains(nextNode.ConfigType)) | ||||||
|             { |             { | ||||||
|                 if (nextOutbound == null) |                 nextOutbound ??= await GenServer(nextNode); | ||||||
|                 { |  | ||||||
|                     nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); |  | ||||||
|                     await GenOutbound(nextNode, nextOutbound); |  | ||||||
|                 } |  | ||||||
|                 nextOutbound.tag = outbound.tag; |                 nextOutbound.tag = outbound.tag; | ||||||
| 
 | 
 | ||||||
|                 outbound.tag = $"mid-{outbound.tag}"; |                 outbound.tag = $"mid-{outbound.tag}"; | ||||||
|  | @ -1115,7 +1329,7 @@ public class CoreConfigSingboxService | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             var dnsOutbound = "dns_out"; |             singboxConfig.route.final = Global.ProxyTag; | ||||||
| 
 | 
 | ||||||
|             if (_config.TunModeItem.EnableTun) |             if (_config.TunModeItem.EnableTun) | ||||||
|             { |             { | ||||||
|  | @ -1131,7 +1345,7 @@ public class CoreConfigSingboxService | ||||||
|                 singboxConfig.route.rules.Add(new() |                 singboxConfig.route.rules.Add(new() | ||||||
|                 { |                 { | ||||||
|                     port = new() { 53 }, |                     port = new() { 53 }, | ||||||
|                     outbound = dnsOutbound, |                     action = "hijack-dns", | ||||||
|                     process_name = lstDnsExe |                     process_name = lstDnsExe | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|  | @ -1142,13 +1356,25 @@ public class CoreConfigSingboxService | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (!_config.Inbound.First().SniffingEnabled) |             if (_config.Inbound.First().SniffingEnabled) | ||||||
|             { |             { | ||||||
|                 singboxConfig.route.rules.Add(new() |                 singboxConfig.route.rules.Add(new() | ||||||
|                 { |                 { | ||||||
|                     port = [53], |                     action = "sniff" | ||||||
|                     network = ["udp"], |                 }); | ||||||
|                     outbound = dnsOutbound |                 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,7 +1389,24 @@ public class CoreConfigSingboxService | ||||||
|                 clash_mode = ERuleMode.Global.ToString() |                 clash_mode = ERuleMode.Global.ToString() | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|  |             var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; | ||||||
|  |             var defaultRouting = await ConfigHandler.GetDefaultRouting(_config); | ||||||
|  |             if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty()) | ||||||
|  |             { | ||||||
|  |                 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 routing = await ConfigHandler.GetDefaultRouting(_config); | ||||||
|  |             var ipRules = new List<RulesItem>(); | ||||||
|             if (routing != null) |             if (routing != null) | ||||||
|             { |             { | ||||||
|                 var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); |                 var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); | ||||||
|  | @ -1172,9 +1415,21 @@ public class CoreConfigSingboxService | ||||||
|                     if (item.Enabled) |                     if (item.Enabled) | ||||||
|                     { |                     { | ||||||
|                         await GenRoutingUserRule(item, singboxConfig); |                         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) |         catch (Exception ex) | ||||||
|         { |         { | ||||||
|  | @ -1222,10 +1477,15 @@ public class CoreConfigSingboxService | ||||||
|             item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig); |             item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig); | ||||||
|             var rules = singboxConfig.route.rules; |             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()) |             if (item.Port.IsNotEmpty()) | ||||||
|             { |             { | ||||||
|  | @ -1349,24 +1609,28 @@ public class CoreConfigSingboxService | ||||||
|         { |         { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         else if (address.StartsWith("geoip:!")) |  | ||||||
|         { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         else if (address.Equals("geoip:private")) |         else if (address.Equals("geoip:private")) | ||||||
|         { |         { | ||||||
|             rule.ip_is_private = true; |             rule.ip_is_private = true; | ||||||
|         } |         } | ||||||
|         else if (address.StartsWith("geoip:")) |         else if (address.StartsWith("geoip:")) | ||||||
|         { |         { | ||||||
|             if (rule.geoip is null) |             rule.geoip ??= new(); | ||||||
|             { rule.geoip = new(); } |  | ||||||
|             rule.geoip?.Add(address.Substring(6)); |             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 |         else | ||||||
|         { |         { | ||||||
|             if (rule.ip_cidr is null) |             rule.ip_cidr ??= new(); | ||||||
|             { rule.ip_cidr = new(); } |  | ||||||
|             rule.ip_cidr?.Add(address); |             rule.ip_cidr?.Add(address); | ||||||
|         } |         } | ||||||
|         return true; |         return true; | ||||||
|  | @ -1381,18 +1645,29 @@ public class CoreConfigSingboxService | ||||||
| 
 | 
 | ||||||
|         var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); |         var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); | ||||||
|         if (node == null |         if (node == null | ||||||
|             || node.ConfigType == EConfigType.Custom) |             || !Global.SingboxSupportConfigType.Contains(node.ConfigType)) | ||||||
|         { |         { | ||||||
|             return Global.ProxyTag; |             return Global.ProxyTag; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); |         var server = await GenServer(node); | ||||||
|         var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); |         if (server is null) | ||||||
|         await GenOutbound(node, outbound); |         { | ||||||
|         outbound.tag = Global.ProxyTag + node.IndexId.ToString(); |             return Global.ProxyTag; | ||||||
|         singboxConfig.outbounds.Add(outbound); |         } | ||||||
| 
 | 
 | ||||||
|         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<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig) |     private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig) | ||||||
|  | @ -1417,7 +1692,14 @@ public class CoreConfigSingboxService | ||||||
|             } |             } | ||||||
|             singboxConfig.dns = dns4Sbox; |             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) |         catch (Exception ex) | ||||||
|         { |         { | ||||||
|  | @ -1432,6 +1714,75 @@ public class CoreConfigSingboxService | ||||||
|         dns4Sbox.servers ??= []; |         dns4Sbox.servers ??= []; | ||||||
|         dns4Sbox.rules ??= []; |         dns4Sbox.rules ??= []; | ||||||
| 
 | 
 | ||||||
|  |         var tag = "local_resolver"; | ||||||
|  |         var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; | ||||||
|  | 
 | ||||||
|  |         if (localDnsAddress.StartsWith("tag://")) | ||||||
|  |         { | ||||||
|  |             tag = localDnsAddress.Substring(6); | ||||||
|  | 
 | ||||||
|  |             var localDnsTag = "local_local"; | ||||||
|  | 
 | ||||||
|  |             dns4Sbox.servers.Add(new() | ||||||
|  |             { | ||||||
|  |                 tag = localDnsTag, | ||||||
|  |                 type = "local" | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             dns4Sbox.rules.Insert(0, new() | ||||||
|  |             { | ||||||
|  |                 server = localDnsTag, | ||||||
|  |                 clash_mode = ERuleMode.Direct.ToString() | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             var (dnsType, dnsHost, dnsPort, dnsPath) = ParseDnsAddress(localDnsAddress); | ||||||
|  | 
 | ||||||
|  |             dns4Sbox.servers.Add(new() | ||||||
|  |             { | ||||||
|  |                 tag = tag, | ||||||
|  |                 type = dnsType, | ||||||
|  |                 server = dnsHost, | ||||||
|  |                 Interface = dnsType == "dhcp" ? dnsHost : null, | ||||||
|  |                 server_port = dnsPort, | ||||||
|  |                 path = dnsPath | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             dns4Sbox.rules.Insert(0, new() | ||||||
|  |             { | ||||||
|  |                 server = tag, | ||||||
|  |                 clash_mode = ERuleMode.Direct.ToString() | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         dns4Sbox.rules.Insert(0, new() | ||||||
|  |         { | ||||||
|  |             server = dns4Sbox.servers.Where(t => t.detour == Global.ProxyTag).Select(t => t.tag).FirstOrDefault() ?? "remote", | ||||||
|  |             clash_mode = ERuleMode.Global.ToString() | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         //Tun2SocksAddress | ||||||
|  |         if (_config.TunModeItem.EnableTun && node?.ConfigType == EConfigType.SOCKS && Utils.IsDomain(node?.Sni)) | ||||||
|  |         { | ||||||
|  |             dns4Sbox.rules.Insert(0, new() | ||||||
|  |             { | ||||||
|  |                 server = tag, | ||||||
|  |                 domain = [node?.Sni], | ||||||
|  |                 strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         singboxConfig.dns = dns4Sbox; | ||||||
|  |         return await Task.FromResult(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private async Task<int> GenDnsDomainsLegacy(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem) | ||||||
|  |     { | ||||||
|  |         var dns4Sbox = singboxConfig.dns ?? new(); | ||||||
|  |         dns4Sbox.servers ??= []; | ||||||
|  |         dns4Sbox.rules ??= []; | ||||||
|  | 
 | ||||||
|         var tag = "local_local"; |         var tag = "local_local"; | ||||||
|         dns4Sbox.servers.Add(new() |         dns4Sbox.servers.Add(new() | ||||||
|         { |         { | ||||||
|  | @ -1479,6 +1830,91 @@ public class CoreConfigSingboxService | ||||||
|         return await Task.FromResult(0); |         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<int> GenExperimental(SingboxConfig singboxConfig) |     private async Task<int> GenExperimental(SingboxConfig singboxConfig) | ||||||
|     { |     { | ||||||
|         //if (_config.guiItem.enableStatistics) |         //if (_config.guiItem.enableStatistics) | ||||||
|  |  | ||||||
|  | @ -1,22 +1,15 @@ | ||||||
|  | using System.Linq; | ||||||
| using System.Net; | using System.Net; | ||||||
| using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||||
| using System.Text.Json.Nodes; | using System.Text.Json.Nodes; | ||||||
| 
 | 
 | ||||||
| namespace ServiceLib.Services.CoreConfig; | 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 |     #region public gen function | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientConfigContent(ProfileItem node) |     public override async Task<RetResult> GenerateClientConfigContent(ProfileItem node) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         try |         try | ||||||
|  | @ -77,7 +70,7 @@ public class CoreConfigV2rayService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad) |     public override async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
| 
 | 
 | ||||||
|  | @ -116,11 +109,7 @@ public class CoreConfigV2rayService | ||||||
|             var proxyProfiles = new List<ProfileItem>(); |             var proxyProfiles = new List<ProfileItem>(); | ||||||
|             foreach (var it in selecteds) |             foreach (var it in selecteds) | ||||||
|             { |             { | ||||||
|                 if (it.ConfigType == EConfigType.Custom) |                 if (!Global.XraySupportConfigType.Contains(it.ConfigType)) | ||||||
|                 { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) |  | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  | @ -206,7 +195,7 @@ public class CoreConfigV2rayService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds) |     public override async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         try |         try | ||||||
|  | @ -255,7 +244,7 @@ public class CoreConfigV2rayService | ||||||
| 
 | 
 | ||||||
|             foreach (var it in selecteds) |             foreach (var it in selecteds) | ||||||
|             { |             { | ||||||
|                 if (it.ConfigType == EConfigType.Custom) |                 if (!Global.SingboxSupportConfigType.Contains(it.ConfigType)) | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  | @ -358,7 +347,7 @@ public class CoreConfigV2rayService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port) |     public override async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         try |         try | ||||||
|  | @ -416,6 +405,80 @@ public class CoreConfigV2rayService | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         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<V2rayConfig>(result); | ||||||
|  |             if (v2rayConfig == null) | ||||||
|  |             { | ||||||
|  |                 ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |                 return ret; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             await GenLog(v2rayConfig); | ||||||
|  | 
 | ||||||
|  |             v2rayConfig.inbounds = new() { new() | ||||||
|  |             { | ||||||
|  |                 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()); | ||||||
|  | 
 | ||||||
|  |             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 = JsonUtils.Serialize(config, true); | ||||||
|  | 
 | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |             ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #endregion public gen function |     #endregion public gen function | ||||||
| 
 | 
 | ||||||
|     #region private gen function |     #region private gen function | ||||||
|  | @ -638,9 +701,7 @@ public class CoreConfigV2rayService | ||||||
| 
 | 
 | ||||||
|         var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); |         var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); | ||||||
|         if (node == null |         if (node == null | ||||||
|             || node.ConfigType == EConfigType.Custom |             || !Global.SingboxSupportConfigType.Contains(node.ConfigType)) | ||||||
|             || node.ConfigType == EConfigType.Hysteria2 |  | ||||||
|             || node.ConfigType == EConfigType.TUIC) |  | ||||||
|         { |         { | ||||||
|             return Global.ProxyTag; |             return Global.ProxyTag; | ||||||
|         } |         } | ||||||
|  | @ -1219,9 +1280,7 @@ public class CoreConfigV2rayService | ||||||
|                 // Previous proxy |                 // Previous proxy | ||||||
|                 var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); |                 var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); | ||||||
|                 if (prevNode is not null |                 if (prevNode is not null | ||||||
|                     && prevNode.ConfigType != EConfigType.Custom |                     && Global.SingboxSupportConfigType.Contains(prevNode.ConfigType) | ||||||
|                     && prevNode.ConfigType != EConfigType.Hysteria2 |  | ||||||
|                     && prevNode.ConfigType != EConfigType.TUIC |  | ||||||
|                     && Utils.IsDomain(prevNode.Address)) |                     && Utils.IsDomain(prevNode.Address)) | ||||||
|                 { |                 { | ||||||
|                     domainList.Add(prevNode.Address); |                     domainList.Add(prevNode.Address); | ||||||
|  | @ -1230,9 +1289,7 @@ public class CoreConfigV2rayService | ||||||
|                 // Next proxy |                 // Next proxy | ||||||
|                 var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); |                 var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); | ||||||
|                 if (nextNode is not null |                 if (nextNode is not null | ||||||
|                     && nextNode.ConfigType != EConfigType.Custom |                     && Global.SingboxSupportConfigType.Contains(nextNode.ConfigType) | ||||||
|                     && nextNode.ConfigType != EConfigType.Hysteria2 |  | ||||||
|                     && nextNode.ConfigType != EConfigType.TUIC |  | ||||||
|                     && Utils.IsDomain(nextNode.Address)) |                     && Utils.IsDomain(nextNode.Address)) | ||||||
|                 { |                 { | ||||||
|                     domainList.Add(nextNode.Address); |                     domainList.Add(nextNode.Address); | ||||||
|  | @ -1348,9 +1405,7 @@ public class CoreConfigV2rayService | ||||||
|             var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); |             var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); | ||||||
|             string? prevOutboundTag = null; |             string? prevOutboundTag = null; | ||||||
|             if (prevNode is not null |             if (prevNode is not null | ||||||
|                 && prevNode.ConfigType != EConfigType.Custom |                 && Global.XraySupportConfigType.Contains(prevNode.ConfigType)) | ||||||
|                 && prevNode.ConfigType != EConfigType.Hysteria2 |  | ||||||
|                 && prevNode.ConfigType != EConfigType.TUIC) |  | ||||||
|             { |             { | ||||||
|                 var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); |                 var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); | ||||||
|                 await GenOutbound(prevNode, prevOutbound); |                 await GenOutbound(prevNode, prevOutbound); | ||||||
|  | @ -1423,9 +1478,7 @@ public class CoreConfigV2rayService | ||||||
|                     { |                     { | ||||||
|                         var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); |                         var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); | ||||||
|                         if (prevNode is not null |                         if (prevNode is not null | ||||||
|                             && prevNode.ConfigType != EConfigType.Custom |                             && !Global.XraySupportConfigType.Contains(prevNode.ConfigType)) | ||||||
|                             && prevNode.ConfigType != EConfigType.Hysteria2 |  | ||||||
|                             && prevNode.ConfigType != EConfigType.TUIC) |  | ||||||
|                         { |                         { | ||||||
|                             var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); |                             var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); | ||||||
|                             await GenOutbound(prevNode, prevOutbound); |                             await GenOutbound(prevNode, prevOutbound); | ||||||
|  | @ -1492,9 +1545,7 @@ public class CoreConfigV2rayService | ||||||
|             // Next proxy |             // Next proxy | ||||||
|             var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); |             var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); | ||||||
|             if (nextNode is not null |             if (nextNode is not null | ||||||
|                 && nextNode.ConfigType != EConfigType.Custom |                 && !Global.XraySupportConfigType.Contains(nextNode.ConfigType)) | ||||||
|                 && nextNode.ConfigType != EConfigType.Hysteria2 |  | ||||||
|                 && nextNode.ConfigType != EConfigType.TUIC) |  | ||||||
|             { |             { | ||||||
|                 if (nextOutbound == null) |                 if (nextOutbound == null) | ||||||
|                 { |                 { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,43 @@ | ||||||
|  | namespace ServiceLib.Services.CoreConfig.Minimal; | ||||||
|  | public class CoreConfigBrookService(Config config) : CoreConfigServiceMinimalBase(config) | ||||||
|  | { | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         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 + ":" + port.ToString(); | ||||||
|  | 
 | ||||||
|  |             // outbound | ||||||
|  |             processArgs += " --server " + node.Address + ":" + node.Port; | ||||||
|  |             processArgs += " --password " + node.Id; | ||||||
|  | 
 | ||||||
|  |             ret.Success = true; | ||||||
|  |             ret.Data = processArgs; | ||||||
|  | 
 | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |             ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,19 +1,21 @@ | ||||||
| namespace ServiceLib.Services.CoreConfig; | namespace ServiceLib.Services.CoreConfig.Minimal; | ||||||
| 
 | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
| /// Core configuration file processing class | /// Core configuration file processing class | ||||||
| /// </summary> | /// </summary> | ||||||
| public class CoreConfigClashService | public class CoreConfigClashService(Config config) : CoreConfigServiceMinimalBase(config) | ||||||
| { | { | ||||||
|     private Config _config; |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|     private static readonly string _tag = "CoreConfigClashService"; |  | ||||||
| 
 |  | ||||||
|     public CoreConfigClashService(Config config) |  | ||||||
|     { |     { | ||||||
|         _config = config; |         var ret = new RetResult | ||||||
|  |         { | ||||||
|  |             Success = false, | ||||||
|  |             Msg = ResUI.OperationFailed | ||||||
|  |         }; | ||||||
|  |         return await Task.FromResult(ret); | ||||||
|     } |     } | ||||||
|   | 
 | ||||||
|     public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName) |     public override async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName) | ||||||
|     { |     { | ||||||
|         var ret = new RetResult(); |         var ret = new RetResult(); | ||||||
|         if (node == null || fileName is null) |         if (node == null || fileName is null) | ||||||
|  | @ -0,0 +1,171 @@ | ||||||
|  | using System.Text.Json.Nodes; | ||||||
|  | 
 | ||||||
|  | namespace ServiceLib.Services.CoreConfig.Minimal; | ||||||
|  | public class CoreConfigHy2Service(Config config) : CoreConfigServiceMinimalBase(config) | ||||||
|  | { | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         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 + ":" + port.ToString() | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // outbound | ||||||
|  |             var outboundPort = string.Empty; | ||||||
|  |             if (node.Ports.IsNotEmpty()) | ||||||
|  |             { | ||||||
|  |                 outboundPort = node.Ports.Replace(':', '-'); | ||||||
|  |                 if (_config.HysteriaItem.HopInterval > 0) | ||||||
|  |                 { | ||||||
|  |                     configJsonNode["transport"] = new JsonObject | ||||||
|  |                     { | ||||||
|  |                         ["udp"] = new JsonObject | ||||||
|  |                         { | ||||||
|  |                             ["hopInterval"] = $"{_config.HysteriaItem.HopInterval}s" | ||||||
|  |                         } | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 outboundPort = node.Port.ToString(); | ||||||
|  |             } | ||||||
|  |             configJsonNode["server"] = node.Address + ":" + outboundPort; | ||||||
|  |             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) | ||||||
|  |             { | ||||||
|  |                 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.Success = true; | ||||||
|  |             ret.Data = JsonUtils.Serialize(configJsonNode, true); | ||||||
|  | 
 | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |             ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public override async Task<RetResult> 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<JsonObject>(fileContent); | ||||||
|  |             if (jsonContent != null) | ||||||
|  |             { | ||||||
|  |                 File.Copy(addressFileName, fileName); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 // If it's YAML, convert to JSON and write it | ||||||
|  |                 var yamlContent = YamlUtils.FromYaml<Dictionary<string, object>>(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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,65 @@ | ||||||
|  | using System.Text.Json.Nodes; | ||||||
|  | 
 | ||||||
|  | namespace ServiceLib.Services.CoreConfig.Minimal; | ||||||
|  | public class CoreConfigJuicityService(Config config) : CoreConfigServiceMinimalBase(config) | ||||||
|  | { | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         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"] = ":" + port.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"] = node.HeaderType; | ||||||
|  | 
 | ||||||
|  |             ret.Success = true; | ||||||
|  |             ret.Data = JsonUtils.Serialize(configJsonNode, true); | ||||||
|  | 
 | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |             ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,44 @@ | ||||||
|  | using System.Text.Json.Nodes; | ||||||
|  | 
 | ||||||
|  | namespace ServiceLib.Services.CoreConfig.Minimal; | ||||||
|  | public class CoreConfigNaiveService(Config config) : CoreConfigServiceMinimalBase(config) | ||||||
|  | { | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         var ret = new RetResult(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             if (node == null | ||||||
|  |                 || node.Port <= 0) | ||||||
|  |             { | ||||||
|  |                 ret.Msg = ResUI.CheckServerSettings; | ||||||
|  |                 return ret; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (node.ConfigType != EConfigType.NaiveProxy) | ||||||
|  |             { | ||||||
|  |                 ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; | ||||||
|  |                 return ret; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             var configJsonNode = new JsonObject(); | ||||||
|  | 
 | ||||||
|  |             // inbound | ||||||
|  |             configJsonNode["listen"] = Global.SocksProtocol + Global.Loopback + ":" + port.ToString(); | ||||||
|  | 
 | ||||||
|  |             // outbound | ||||||
|  |             configJsonNode["proxy"] = (node.HeaderType == "quic" ? "quic://" : Global.HttpsProtocol) + node.Id + "@" + node.Address + ":" + node.Port; | ||||||
|  | 
 | ||||||
|  |             ret.Success = true; | ||||||
|  |             ret.Data = JsonUtils.Serialize(configJsonNode, true); | ||||||
|  | 
 | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |             ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,86 @@ | ||||||
|  | using System.Text.Json.Nodes; | ||||||
|  | 
 | ||||||
|  | namespace ServiceLib.Services.CoreConfig.Minimal; | ||||||
|  | public class CoreConfigShadowquicService(Config config) : CoreConfigServiceMinimalBase(config) | ||||||
|  | { | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         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<string, object>(); | ||||||
|  | 
 | ||||||
|  |             // 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<string, object> | ||||||
|  |             { | ||||||
|  |                 ["type"] = "socks5", | ||||||
|  |                 ["listen"] = Global.Loopback + ":" + port.ToString() | ||||||
|  |             }; | ||||||
|  |             configYamlNode["inbound"] = inboundNode; | ||||||
|  | 
 | ||||||
|  |             // outbound | ||||||
|  |             var alpn = new List<string>(); | ||||||
|  |             foreach (var item in node.GetAlpn() ?? new List<string>()) | ||||||
|  |             { | ||||||
|  |                 alpn.Add(item); | ||||||
|  |             } | ||||||
|  |             if (alpn.Count == 0) | ||||||
|  |             { | ||||||
|  |                 alpn.Add("h3"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             var outboundNode = new Dictionary<string, object> | ||||||
|  |             { | ||||||
|  |                 ["type"] = "shadowquic", | ||||||
|  |                 ["addr"] = node.Address + ":" + node.Port, | ||||||
|  |                 ["password"] = node.Id, | ||||||
|  |                 ["username"] = node.Security, | ||||||
|  |                 ["alpn"] = alpn, | ||||||
|  |                 ["congestion-control"] = node.HeaderType, | ||||||
|  |                 ["zero-rtt"] = true | ||||||
|  |             }; | ||||||
|  |             if (node.Sni.IsNotEmpty()) | ||||||
|  |             { | ||||||
|  |                 outboundNode["server-name"] = node.Sni; | ||||||
|  |             } | ||||||
|  |             configYamlNode["outbound"] = outboundNode; | ||||||
|  | 
 | ||||||
|  |             ret.Success = true; | ||||||
|  |             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); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | using System.Text.Json.Nodes; | ||||||
|  | 
 | ||||||
|  | namespace ServiceLib.Services.CoreConfig.Minimal; | ||||||
|  | public class CoreConfigTuicService(Config config) : CoreConfigServiceMinimalBase(config) | ||||||
|  | { | ||||||
|  |     protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port) | ||||||
|  |     { | ||||||
|  |         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 + ":" + port.ToString() | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // outbound | ||||||
|  |             var alpn = new JsonArray(); | ||||||
|  |             foreach(var item in node.GetAlpn() ?? new List<string>()) | ||||||
|  |             { | ||||||
|  |                 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"] = node.HeaderType, | ||||||
|  |                 ["alpn"] = alpn | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             ret.Success = true; | ||||||
|  |             ret.Data = JsonUtils.Serialize(configJsonNode, true); | ||||||
|  | 
 | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logging.SaveLog(_tag, ex); | ||||||
|  |             ret.Msg = ResUI.FailedGenDefaultConfiguration; | ||||||
|  |             return await Task.FromResult(ret); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
|  | using System.Linq; | ||||||
| using System.Net; | using System.Net; | ||||||
| using System.Net.Sockets; | using System.Net.Sockets; | ||||||
| 
 | 
 | ||||||
|  | @ -70,7 +71,7 @@ public class SpeedtestService | ||||||
|         var lstSelected = new List<ServerTestItem>(); |         var lstSelected = new List<ServerTestItem>(); | ||||||
|         foreach (var it in selecteds) |         foreach (var it in selecteds) | ||||||
|         { |         { | ||||||
|             if (it.ConfigType == EConfigType.Custom) |             if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) | ||||||
|             { |             { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  | @ -122,7 +123,7 @@ public class SpeedtestService | ||||||
|         List<Task> tasks = []; |         List<Task> tasks = []; | ||||||
|         foreach (var it in selecteds) |         foreach (var it in selecteds) | ||||||
|         { |         { | ||||||
|             if (it.ConfigType == EConfigType.Custom) |             if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) | ||||||
|             { |             { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  | @ -207,7 +208,7 @@ public class SpeedtestService | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|                 if (it.ConfigType == EConfigType.Custom) |                 if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  | @ -244,7 +245,7 @@ public class SpeedtestService | ||||||
|                 UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); |                 UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             if (it.ConfigType == EConfigType.Custom) |             if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) | ||||||
|             { |             { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  | @ -358,8 +359,8 @@ public class SpeedtestService | ||||||
|     private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize) |     private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize) | ||||||
|     { |     { | ||||||
|         List<List<ServerTestItem>> lstTest = new(); |         List<List<ServerTestItem>> lstTest = new(); | ||||||
|         var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC)).ToList(); |         var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList(); | ||||||
|         var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC).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++) |         for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++) | ||||||
|         { |         { | ||||||
|  |  | ||||||
|  | @ -20,10 +20,15 @@ public class MainWindowViewModel : MyReactiveObject | ||||||
|     public ReactiveCommand<Unit, Unit> AddHysteria2ServerCmd { get; } |     public ReactiveCommand<Unit, Unit> AddHysteria2ServerCmd { get; } | ||||||
|     public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; } |     public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; } | ||||||
|     public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; } |     public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; } | ||||||
|  |     public ReactiveCommand<Unit, Unit> AddAnytlsServerCmd { get; } | ||||||
|     public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; } |     public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; } | ||||||
|     public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; } |     public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; } | ||||||
|     public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; } |     public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; } | ||||||
|     public ReactiveCommand<Unit, Unit> AddServerViaImageCmd { get; } |     public ReactiveCommand<Unit, Unit> AddServerViaImageCmd { get; } | ||||||
|  |     public ReactiveCommand<Unit, Unit> AddBrookServerCmd { get; } | ||||||
|  |     public ReactiveCommand<Unit, Unit> AddJuicityServerCmd { get; } | ||||||
|  |     public ReactiveCommand<Unit, Unit> AddNaiveServerCmd { get; } | ||||||
|  |     public ReactiveCommand<Unit, Unit> AddShadowquicServerCmd { get; } | ||||||
| 
 | 
 | ||||||
|     //Subscription |     //Subscription | ||||||
|     public ReactiveCommand<Unit, Unit> SubSettingCmd { get; } |     public ReactiveCommand<Unit, Unit> SubSettingCmd { get; } | ||||||
|  | @ -111,6 +116,26 @@ public class MainWindowViewModel : MyReactiveObject | ||||||
|         { |         { | ||||||
|             await AddServerAsync(true, EConfigType.WireGuard); |             await AddServerAsync(true, EConfigType.WireGuard); | ||||||
|         }); |         }); | ||||||
|  |         AddAnytlsServerCmd = ReactiveCommand.CreateFromTask(async () => | ||||||
|  |         { | ||||||
|  |             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 () => |         AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () => | ||||||
|         { |         { | ||||||
|             await AddServerAsync(true, EConfigType.Custom); |             await AddServerAsync(true, EConfigType.Custom); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| using System.Reactive; | using System.Reactive; | ||||||
| using ReactiveUI; | using ReactiveUI; | ||||||
| using ReactiveUI.Fody.Helpers; | using ReactiveUI.Fody.Helpers; | ||||||
|  | using ServiceLib.Models; | ||||||
| 
 | 
 | ||||||
| namespace ServiceLib.ViewModels; | namespace ServiceLib.ViewModels; | ||||||
| 
 | 
 | ||||||
|  | @ -104,6 +105,21 @@ public class OptionSettingViewModel : MyReactiveObject | ||||||
| 
 | 
 | ||||||
|     #endregion CoreType |     #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<Unit, Unit> SaveCmd { get; } |     public ReactiveCommand<Unit, Unit> SaveCmd { get; } | ||||||
| 
 | 
 | ||||||
|     public OptionSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView) |     public OptionSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView) | ||||||
|  | @ -210,14 +226,13 @@ public class OptionSettingViewModel : MyReactiveObject | ||||||
|         #endregion Tun mode |         #endregion Tun mode | ||||||
| 
 | 
 | ||||||
|         await InitCoreType(); |         await InitCoreType(); | ||||||
|  | 
 | ||||||
|  |         await InitSplitCoreItem(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async Task InitCoreType() |     private async Task InitCoreType() | ||||||
|     { |     { | ||||||
|         if (_config.CoreTypeItem == null) |         _config.CoreTypeItem ??= new List<CoreTypeItem>(); | ||||||
|         { |  | ||||||
|             _config.CoreTypeItem = new List<CoreTypeItem>(); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) |         foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) | ||||||
|         { |         { | ||||||
|  | @ -232,6 +247,7 @@ public class OptionSettingViewModel : MyReactiveObject | ||||||
|                 CoreType = ECoreType.Xray |                 CoreType = ECoreType.Xray | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         _config.CoreTypeItem.ForEach(it => |         _config.CoreTypeItem.ForEach(it => | ||||||
|         { |         { | ||||||
|             var type = it.CoreType.ToString(); |             var type = it.CoreType.ToString(); | ||||||
|  | @ -269,6 +285,87 @@ public class OptionSettingViewModel : MyReactiveObject | ||||||
|         await Task.CompletedTask; |         await Task.CompletedTask; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private async Task InitSplitCoreItem() | ||||||
|  |     { | ||||||
|  |         _config.SplitCoreItem ??= new() | ||||||
|  |         { | ||||||
|  |             EnableSplitCore = false, | ||||||
|  |             SplitCoreTypes = new List<CoreTypeItem>(), | ||||||
|  |             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() |     private async Task SaveSettingAsync() | ||||||
|     { |     { | ||||||
|         if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString()) |         if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString()) | ||||||
|  | @ -362,6 +459,7 @@ public class OptionSettingViewModel : MyReactiveObject | ||||||
| 
 | 
 | ||||||
|         //coreType |         //coreType | ||||||
|         await SaveCoreType(); |         await SaveCoreType(); | ||||||
|  |         await SaveSplitCoreType(); | ||||||
| 
 | 
 | ||||||
|         if (await ConfigHandler.SaveConfig(_config) == 0) |         if (await ConfigHandler.SaveConfig(_config) == 0) | ||||||
|         { |         { | ||||||
|  | @ -420,4 +518,46 @@ public class OptionSettingViewModel : MyReactiveObject | ||||||
|         } |         } | ||||||
|         await Task.CompletedTask; |         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; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -770,7 +770,8 @@ public class ProfilesViewModel : MyReactiveObject | ||||||
|         } |         } | ||||||
|         if (blClipboard) |         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) |             if (result.Success != true) | ||||||
|             { |             { | ||||||
|                 NoticeHandler.Instance.Enqueue(result.Msg); |                 NoticeHandler.Instance.Enqueue(result.Msg); | ||||||
|  | @ -793,7 +794,8 @@ public class ProfilesViewModel : MyReactiveObject | ||||||
|         { |         { | ||||||
|             return; |             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) |         if (result.Success != true) | ||||||
|         { |         { | ||||||
|             NoticeHandler.Instance.Enqueue(result.Msg); |             NoticeHandler.Instance.Enqueue(result.Msg); | ||||||
|  |  | ||||||
|  | @ -533,6 +533,171 @@ | ||||||
|                         HorizontalAlignment="Left" |                         HorizontalAlignment="Left" | ||||||
|                         Watermark="1500" /> |                         Watermark="1500" /> | ||||||
|                 </Grid> |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridAnytls" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     ColumnDefinitions="180,Auto" | ||||||
|  |                     IsVisible="False" | ||||||
|  |                     RowDefinitions="Auto,Auto,Auto"> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId10" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridNaive" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     ColumnDefinitions="180,Auto" | ||||||
|  |                     IsVisible="False" | ||||||
|  |                     RowDefinitions="Auto,Auto,Auto"> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId100" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbHeaderType100}" /> | ||||||
|  |                     <ComboBox | ||||||
|  |                         x:Name="cmbHeaderType100" | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="200" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridJuicity" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     ColumnDefinitions="180,Auto" | ||||||
|  |                     IsVisible="False" | ||||||
|  |                     RowDefinitions="Auto,Auto,Auto,Auto"> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId101" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtSecurity101" | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbHeaderType8}" /> | ||||||
|  |                     <ComboBox | ||||||
|  |                         x:Name="cmbHeaderType101" | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="200" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridBrook" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     ColumnDefinitions="180,Auto" | ||||||
|  |                     IsVisible="False" | ||||||
|  |                     RowDefinitions="Auto,Auto,Auto"> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId102" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridShadowquic" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     ColumnDefinitions="180,Auto" | ||||||
|  |                     IsVisible="False" | ||||||
|  |                     RowDefinitions="Auto,Auto,Auto,Auto"> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId103" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtSecurity103" | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbHeaderType8}" /> | ||||||
|  |                     <ComboBox | ||||||
|  |                         x:Name="cmbHeaderType103" | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="200" | ||||||
|  |                         Margin="{StaticResource Margin4}" /> | ||||||
|  |                 </Grid> | ||||||
| 
 | 
 | ||||||
|                 <Separator |                 <Separator | ||||||
|                     x:Name="sepa2" |                     x:Name="sepa2" | ||||||
|  |  | ||||||
|  | @ -78,7 +78,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel> | ||||||
|                 gridHysteria2.IsVisible = true; |                 gridHysteria2.IsVisible = true; | ||||||
|                 sepa2.IsVisible = false; |                 sepa2.IsVisible = false; | ||||||
|                 gridTransport.IsVisible = false; |                 gridTransport.IsVisible = false; | ||||||
|                 cmbCoreType.IsEnabled = false; |                 cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes.AppendEmpty(); | ||||||
|                 cmbFingerprint.IsEnabled = false; |                 cmbFingerprint.IsEnabled = false; | ||||||
|                 cmbFingerprint.SelectedValue = string.Empty; |                 cmbFingerprint.SelectedValue = string.Empty; | ||||||
|                 break; |                 break; | ||||||
|  | @ -87,11 +87,11 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel> | ||||||
|                 gridTuic.IsVisible = true; |                 gridTuic.IsVisible = true; | ||||||
|                 sepa2.IsVisible = false; |                 sepa2.IsVisible = false; | ||||||
|                 gridTransport.IsVisible = false; |                 gridTransport.IsVisible = false; | ||||||
|                 cmbCoreType.IsEnabled = false; |                 cmbCoreType.ItemsSource = Global.TuicCoreTypes.AppendEmpty(); | ||||||
|                 cmbFingerprint.IsEnabled = false; |                 cmbFingerprint.IsEnabled = false; | ||||||
|                 cmbFingerprint.SelectedValue = string.Empty; |                 cmbFingerprint.SelectedValue = string.Empty; | ||||||
| 
 | 
 | ||||||
|                 cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; |                 cmbHeaderType8.ItemsSource = Global.CongestionControls; | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             case EConfigType.WireGuard: |             case EConfigType.WireGuard: | ||||||
|  | @ -102,6 +102,55 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel> | ||||||
|                 gridTls.IsVisible = false; |                 gridTls.IsVisible = false; | ||||||
| 
 | 
 | ||||||
|                 break; |                 break; | ||||||
|  | 
 | ||||||
|  |             case EConfigType.Anytls: | ||||||
|  |                 gridAnytls.IsVisible = true; | ||||||
|  |                 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; |         cmbStreamSecurity.ItemsSource = lstStreamSecurity; | ||||||
| 
 | 
 | ||||||
|  | @ -167,6 +216,31 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel> | ||||||
|                     this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); |                     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); |                     this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); | ||||||
|                     break; |                     break; | ||||||
|  | 
 | ||||||
|  |                 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.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables); | ||||||
|  |  | ||||||
|  | @ -46,6 +46,12 @@ | ||||||
|                         <Separator /> |                         <Separator /> | ||||||
|                         <MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" /> |                         <MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" /> | ||||||
|                         <MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" /> |                         <MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" /> | ||||||
|  |                         <MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" /> | ||||||
|  |                         <Separator /> | ||||||
|  |                         <MenuItem x:Name="menuAddBrookServer" Header="{x:Static resx:ResUI.menuAddBrookServer}" /> | ||||||
|  |                         <MenuItem x:Name="menuAddJuicityServer" Header="{x:Static resx:ResUI.menuAddJuicityServer}" /> | ||||||
|  |                         <MenuItem x:Name="menuAddNaiveServer" Header="{x:Static resx:ResUI.menuAddNaiveServer}" /> | ||||||
|  |                         <MenuItem x:Name="menuAddShadowquicServer" Header="{x:Static resx:ResUI.menuAddShadowquicServer}" /> | ||||||
|                     </MenuItem> |                     </MenuItem> | ||||||
| 
 | 
 | ||||||
|                     <MenuItem Padding="8,0"> |                     <MenuItem Padding="8,0"> | ||||||
|  |  | ||||||
|  | @ -83,6 +83,11 @@ public partial class MainWindow : WindowBase<MainWindowViewModel> | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables); |             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.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).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.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); |             this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); |             this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); | ||||||
|  |  | ||||||
|  | @ -802,101 +802,298 @@ | ||||||
|             </TabItem> |             </TabItem> | ||||||
| 
 | 
 | ||||||
|             <TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}"> |             <TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}"> | ||||||
|                 <Grid |                 <ScrollViewer VerticalScrollBarVisibility="Visible"> | ||||||
|                     Margin="{StaticResource Margin4}" |                     <Grid Margin="{StaticResource Margin4}"> | ||||||
|                     ColumnDefinitions="Auto,Auto" |                         <Grid.RowDefinitions> | ||||||
|                     RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> |                             <RowDefinition Height="Auto" /> | ||||||
|                     <TextBlock |                         </Grid.RowDefinitions> | ||||||
|                         Grid.Row="1" |                         <Grid.ColumnDefinitions> | ||||||
|                         Grid.Column="0" |                             <ColumnDefinition Width="Auto" /> | ||||||
|                         Margin="{StaticResource Margin4}" |                             <ColumnDefinition Width="Auto" /> | ||||||
|                         VerticalAlignment="Center" |                         </Grid.ColumnDefinitions> | ||||||
|                         Text="VMess" /> |  | ||||||
|                     <ComboBox |  | ||||||
|                         x:Name="cmbCoreType1" |  | ||||||
|                         Grid.Row="1" |  | ||||||
|                         Grid.Column="1" |  | ||||||
|                         Width="200" |  | ||||||
|                         Margin="{StaticResource Margin4}" /> |  | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                         <Grid | ||||||
|                         Grid.Row="2" |                             Grid.Row="0" | ||||||
|                         Grid.Column="0" |                             Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin4}" |                             Margin="{StaticResource Margin4}"> | ||||||
|                         VerticalAlignment="Center" |                             <Grid.RowDefinitions> | ||||||
|                         Text="Custom" /> |                                 <RowDefinition Height="Auto" /> | ||||||
|                     <ComboBox |                                 <RowDefinition Height="Auto" /> | ||||||
|                         x:Name="cmbCoreType2" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Grid.Row="2" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Grid.Column="1" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Width="200" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Margin="{StaticResource Margin4}" /> |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                             </Grid.RowDefinitions> | ||||||
|  |                             <Grid.ColumnDefinitions> | ||||||
|  |                                 <ColumnDefinition Width="Auto" /> | ||||||
|  |                                 <ColumnDefinition Width="Auto" /> | ||||||
|  |                             </Grid.ColumnDefinitions> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="3" |                                 Grid.Row="1" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin4}" |                                 Margin="{StaticResource Margin4}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Text="Shadowsocks" /> |                                 Text="VMess" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType3" |                                 x:Name="cmbCoreType1" | ||||||
|                         Grid.Row="3" |                                 Grid.Row="1" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin4}" /> |                                 Margin="{StaticResource Margin4}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="4" |                                 Grid.Row="2" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin4}" |                                 Margin="{StaticResource Margin4}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Text="Socks" /> |                                 Text="Custom" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType4" |                                 x:Name="cmbCoreType2" | ||||||
|                         Grid.Row="4" |                                 Grid.Row="2" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin4}" /> |                                 Margin="{StaticResource Margin4}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="5" |                                 Grid.Row="3" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin4}" |                                 Margin="{StaticResource Margin4}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Text="VLESS" /> |                                 Text="Shadowsocks" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType5" |                                 x:Name="cmbCoreType3" | ||||||
|                         Grid.Row="5" |                                 Grid.Row="3" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin4}" /> |                                 Margin="{StaticResource Margin4}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="6" |                                 Grid.Row="4" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin4}" |                                 Margin="{StaticResource Margin4}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Text="Trojan" /> |                                 Text="Socks" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType6" |                                 x:Name="cmbCoreType4" | ||||||
|                         Grid.Row="6" |                                 Grid.Row="4" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin4}" /> |                                 Margin="{StaticResource Margin4}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="7" |                                 Grid.Row="5" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin4}" |                                 Margin="{StaticResource Margin4}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Text="Wireguard" /> |                                 Text="VLESS" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType9" |                                 x:Name="cmbCoreType5" | ||||||
|                         Grid.Row="7" |                                 Grid.Row="5" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin4}" /> |                                 Margin="{StaticResource Margin4}" /> | ||||||
|                 </Grid> | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="6" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="Trojan" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreType6" | ||||||
|  |                                 Grid.Row="6" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="7" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="Wireguard" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreType9" | ||||||
|  |                                 Grid.Row="7" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  |                         </Grid> | ||||||
|  | 
 | ||||||
|  |                         <Grid | ||||||
|  |                             Grid.Row="0" | ||||||
|  |                             Grid.Column="1" | ||||||
|  |                             Margin="{StaticResource Margin4}"> | ||||||
|  |                             <Grid.RowDefinitions> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                             </Grid.RowDefinitions> | ||||||
|  |                             <Grid.ColumnDefinitions> | ||||||
|  |                                 <ColumnDefinition Width="Auto" /> | ||||||
|  |                                 <ColumnDefinition Width="Auto" /> | ||||||
|  |                             </Grid.ColumnDefinitions> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="0" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Grid.ColumnSpan="2" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc1}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="1" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Grid.ColumnSpan="2" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc2}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="2" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSettingsSplitCoreEnable}" /> | ||||||
|  |                             <ToggleSwitch | ||||||
|  |                                 x:Name="togCoreSplit" | ||||||
|  |                                 Grid.Row="2" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 HorizontalAlignment="Left" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="3" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="路由 Core" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitRouteType" | ||||||
|  |                                 Grid.Row="3" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="4" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="VMess" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType1" | ||||||
|  |                                 Grid.Row="4" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="5" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="Shadowsocks" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType3" | ||||||
|  |                                 Grid.Row="5" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="6" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="Socks" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType4" | ||||||
|  |                                 Grid.Row="6" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="7" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="VLESS" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType5" | ||||||
|  |                                 Grid.Row="7" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="8" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="Trojan" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType6" | ||||||
|  |                                 Grid.Row="8" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="9" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="Hysteria2" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType7" | ||||||
|  |                                 Grid.Row="9" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="10" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="TUIC" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType8" | ||||||
|  |                                 Grid.Row="10" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="11" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin4}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Text="Wireguard" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType9" | ||||||
|  |                                 Grid.Row="11" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin4}" /> | ||||||
|  |                         </Grid> | ||||||
|  |                     </Grid> | ||||||
|  |                 </ScrollViewer> | ||||||
|             </TabItem> |             </TabItem> | ||||||
|         </TabControl> |         </TabControl> | ||||||
|     </DockPanel> |     </DockPanel> | ||||||
|  |  | ||||||
|  | @ -41,6 +41,17 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel> | ||||||
|         cmbCoreType6.ItemsSource = Global.CoreTypes; |         cmbCoreType6.ItemsSource = Global.CoreTypes; | ||||||
|         cmbCoreType9.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(); |         cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList(); | ||||||
|         cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); |         cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); | ||||||
|         cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls; |         cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls; | ||||||
|  | @ -119,6 +130,18 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel> | ||||||
|             this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables); |             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.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); |             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -117,7 +117,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel> | ||||||
| 
 | 
 | ||||||
|     private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e) |     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) |     private void btnCancel_Click(object? sender, RoutedEventArgs e) | ||||||
|  |  | ||||||
|  | @ -707,6 +707,228 @@ | ||||||
|                         materialDesign:HintAssist.Hint="1500" |                         materialDesign:HintAssist.Hint="1500" | ||||||
|                         Style="{StaticResource DefTextBox}" /> |                         Style="{StaticResource DefTextBox}" /> | ||||||
|                 </Grid> |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridAnytls" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     Visibility="Hidden"> | ||||||
|  |                     <Grid.RowDefinitions> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                     </Grid.RowDefinitions> | ||||||
|  |                     <Grid.ColumnDefinitions> | ||||||
|  |                         <ColumnDefinition Width="180" /> | ||||||
|  |                         <ColumnDefinition Width="Auto" /> | ||||||
|  |                     </Grid.ColumnDefinitions> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId10" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefTextBox}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridNaive" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     Visibility="Hidden"> | ||||||
|  |                     <Grid.RowDefinitions> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                     </Grid.RowDefinitions> | ||||||
|  |                     <Grid.ColumnDefinitions> | ||||||
|  |                         <ColumnDefinition Width="180" /> | ||||||
|  |                         <ColumnDefinition Width="Auto" /> | ||||||
|  |                     </Grid.ColumnDefinitions> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId100" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefTextBox}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbHeaderType100}" /> | ||||||
|  |                     <ComboBox | ||||||
|  |                         x:Name="cmbHeaderType100" | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="200" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefComboBox}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridJuicity" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     Visibility="Hidden"> | ||||||
|  |                     <Grid.RowDefinitions> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                     </Grid.RowDefinitions> | ||||||
|  |                     <Grid.ColumnDefinitions> | ||||||
|  |                         <ColumnDefinition Width="180" /> | ||||||
|  |                         <ColumnDefinition Width="Auto" /> | ||||||
|  |                     </Grid.ColumnDefinitions> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId101" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefTextBox}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtSecurity101" | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefTextBox}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbHeaderType8}" /> | ||||||
|  |                     <ComboBox | ||||||
|  |                         x:Name="cmbHeaderType101" | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="200" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefComboBox}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridBrook" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     Visibility="Hidden"> | ||||||
|  |                     <Grid.RowDefinitions> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                     </Grid.RowDefinitions> | ||||||
|  |                     <Grid.ColumnDefinitions> | ||||||
|  |                         <ColumnDefinition Width="180" /> | ||||||
|  |                         <ColumnDefinition Width="Auto" /> | ||||||
|  |                     </Grid.ColumnDefinitions> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId102" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefTextBox}" /> | ||||||
|  |                 </Grid> | ||||||
|  |                 <Grid | ||||||
|  |                     x:Name="gridShadowquic" | ||||||
|  |                     Grid.Row="2" | ||||||
|  |                     Visibility="Hidden"> | ||||||
|  |                     <Grid.RowDefinitions> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                         <RowDefinition Height="Auto" /> | ||||||
|  |                     </Grid.RowDefinitions> | ||||||
|  |                     <Grid.ColumnDefinitions> | ||||||
|  |                         <ColumnDefinition Width="180" /> | ||||||
|  |                         <ColumnDefinition Width="Auto" /> | ||||||
|  |                     </Grid.ColumnDefinitions> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtId103" | ||||||
|  |                         Grid.Row="1" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefTextBox}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbId3}" /> | ||||||
|  |                     <TextBox | ||||||
|  |                         x:Name="txtSecurity103" | ||||||
|  |                         Grid.Row="2" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="400" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefTextBox}" /> | ||||||
|  | 
 | ||||||
|  |                     <TextBlock | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="0" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         VerticalAlignment="Center" | ||||||
|  |                         Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                         Text="{x:Static resx:ResUI.TbHeaderType8}" /> | ||||||
|  |                     <ComboBox | ||||||
|  |                         x:Name="cmbHeaderType103" | ||||||
|  |                         Grid.Row="3" | ||||||
|  |                         Grid.Column="1" | ||||||
|  |                         Width="200" | ||||||
|  |                         Margin="{StaticResource Margin4}" | ||||||
|  |                         Style="{StaticResource DefComboBox}" /> | ||||||
|  |                 </Grid> | ||||||
| 
 | 
 | ||||||
|                 <Separator |                 <Separator | ||||||
|                     x:Name="sepa2" |                     x:Name="sepa2" | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ public partial class AddServerWindow | ||||||
|                 gridHysteria2.Visibility = Visibility.Visible; |                 gridHysteria2.Visibility = Visibility.Visible; | ||||||
|                 sepa2.Visibility = Visibility.Collapsed; |                 sepa2.Visibility = Visibility.Collapsed; | ||||||
|                 gridTransport.Visibility = Visibility.Collapsed; |                 gridTransport.Visibility = Visibility.Collapsed; | ||||||
|                 cmbCoreType.IsEnabled = false; |                 cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes.AppendEmpty(); | ||||||
|                 cmbFingerprint.IsEnabled = false; |                 cmbFingerprint.IsEnabled = false; | ||||||
|                 cmbFingerprint.Text = string.Empty; |                 cmbFingerprint.Text = string.Empty; | ||||||
|                 break; |                 break; | ||||||
|  | @ -81,11 +81,11 @@ public partial class AddServerWindow | ||||||
|                 gridTuic.Visibility = Visibility.Visible; |                 gridTuic.Visibility = Visibility.Visible; | ||||||
|                 sepa2.Visibility = Visibility.Collapsed; |                 sepa2.Visibility = Visibility.Collapsed; | ||||||
|                 gridTransport.Visibility = Visibility.Collapsed; |                 gridTransport.Visibility = Visibility.Collapsed; | ||||||
|                 cmbCoreType.IsEnabled = false; |                 cmbCoreType.ItemsSource = Global.TuicCoreTypes.AppendEmpty(); | ||||||
|                 cmbFingerprint.IsEnabled = false; |                 cmbFingerprint.IsEnabled = false; | ||||||
|                 cmbFingerprint.Text = string.Empty; |                 cmbFingerprint.Text = string.Empty; | ||||||
| 
 | 
 | ||||||
|                 cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; |                 cmbHeaderType8.ItemsSource = Global.CongestionControls; | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             case EConfigType.WireGuard: |             case EConfigType.WireGuard: | ||||||
|  | @ -96,6 +96,55 @@ public partial class AddServerWindow | ||||||
|                 gridTls.Visibility = Visibility.Collapsed; |                 gridTls.Visibility = Visibility.Collapsed; | ||||||
| 
 | 
 | ||||||
|                 break; |                 break; | ||||||
|  | 
 | ||||||
|  |             case EConfigType.Anytls: | ||||||
|  |                 gridAnytls.Visibility = Visibility.Visible; | ||||||
|  |                 cmbCoreType.IsEnabled = false; | ||||||
|  |                 lstStreamSecurity.Add(Global.StreamSecurityReality); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case EConfigType.NaiveProxy: | ||||||
|  |                 gridNaive.Visibility = Visibility.Visible; | ||||||
|  |                 sepa2.Visibility = Visibility.Collapsed; | ||||||
|  |                 gridTransport.Visibility = Visibility.Collapsed; | ||||||
|  |                 cmbAlpn.IsEnabled = false; | ||||||
|  |                 cmbFingerprint.IsEnabled = false; | ||||||
|  |                 cmbFingerprint.Text = string.Empty; | ||||||
|  |                 cmbCoreType.IsEnabled = false; | ||||||
|  | 
 | ||||||
|  |                 cmbHeaderType100.ItemsSource = Global.NaiveProxyProtocols; | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case EConfigType.Juicity: | ||||||
|  |                 gridJuicity.Visibility = Visibility.Visible; | ||||||
|  |                 sepa2.Visibility = Visibility.Collapsed; | ||||||
|  |                 gridTransport.Visibility = Visibility.Collapsed; | ||||||
|  |                 cmbAlpn.IsEnabled = false; | ||||||
|  |                 cmbFingerprint.IsEnabled = false; | ||||||
|  |                 cmbFingerprint.Text = string.Empty; | ||||||
|  |                 cmbCoreType.IsEnabled = false; | ||||||
|  | 
 | ||||||
|  |                 cmbHeaderType101.ItemsSource = Global.CongestionControls; | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case EConfigType.Brook: | ||||||
|  |                 gridBrook.Visibility = Visibility.Visible; | ||||||
|  |                 sepa2.Visibility = Visibility.Collapsed; | ||||||
|  |                 gridTransport.Visibility = Visibility.Collapsed; | ||||||
|  |                 gridTls.Visibility = Visibility.Collapsed; | ||||||
|  |                 cmbCoreType.IsEnabled = false; | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case EConfigType.Shadowquic: | ||||||
|  |                 gridShadowquic.Visibility = Visibility.Visible; | ||||||
|  |                 sepa2.Visibility = Visibility.Collapsed; | ||||||
|  |                 gridTransport.Visibility = Visibility.Collapsed; | ||||||
|  |                 cmbFingerprint.IsEnabled = false; | ||||||
|  |                 cmbFingerprint.Text = string.Empty; | ||||||
|  |                 cmbCoreType.IsEnabled = false; | ||||||
|  | 
 | ||||||
|  |                 cmbHeaderType103.ItemsSource = Global.CongestionControls; | ||||||
|  |                 break; | ||||||
|         } |         } | ||||||
|         cmbStreamSecurity.ItemsSource = lstStreamSecurity; |         cmbStreamSecurity.ItemsSource = lstStreamSecurity; | ||||||
| 
 | 
 | ||||||
|  | @ -161,6 +210,31 @@ public partial class AddServerWindow | ||||||
|                     this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); |                     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); |                     this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); | ||||||
|                     break; |                     break; | ||||||
|  | 
 | ||||||
|  |                 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.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.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables); |             this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables); | ||||||
|  |  | ||||||
|  | @ -108,6 +108,27 @@ | ||||||
|                                     x:Name="menuAddTuicServer" |                                     x:Name="menuAddTuicServer" | ||||||
|                                     Height="{StaticResource MenuItemHeight}" |                                     Height="{StaticResource MenuItemHeight}" | ||||||
|                                     Header="{x:Static resx:ResUI.menuAddTuicServer}" /> |                                     Header="{x:Static resx:ResUI.menuAddTuicServer}" /> | ||||||
|  |                                 <MenuItem | ||||||
|  |                                     x:Name="menuAddAnytlsServer" | ||||||
|  |                                     Height="{StaticResource MenuItemHeight}" | ||||||
|  |                                     Header="{x:Static resx:ResUI.menuAddAnytlsServer}" /> | ||||||
|  |                                 <Separator Margin="-40,5" /> | ||||||
|  |                                 <MenuItem | ||||||
|  |                                     x:Name="menuAddBrookServer" | ||||||
|  |                                     Height="{StaticResource MenuItemHeight}" | ||||||
|  |                                     Header="{x:Static resx:ResUI.menuAddBrookServer}" /> | ||||||
|  |                                 <MenuItem | ||||||
|  |                                     x:Name="menuAddJuicityServer" | ||||||
|  |                                     Height="{StaticResource MenuItemHeight}" | ||||||
|  |                                     Header="{x:Static resx:ResUI.menuAddJuicityServer}" /> | ||||||
|  |                                 <MenuItem | ||||||
|  |                                     x:Name="menuAddNaiveServer" | ||||||
|  |                                     Height="{StaticResource MenuItemHeight}" | ||||||
|  |                                     Header="{x:Static resx:ResUI.menuAddNaiveServer}" /> | ||||||
|  |                                 <MenuItem | ||||||
|  |                                     x:Name="menuAddShadowquicServer" | ||||||
|  |                                     Height="{StaticResource MenuItemHeight}" | ||||||
|  |                                     Header="{x:Static resx:ResUI.menuAddShadowquicServer}" /> | ||||||
|                             </MenuItem> |                             </MenuItem> | ||||||
|                         </Menu> |                         </Menu> | ||||||
|                         <Separator /> |                         <Separator /> | ||||||
|  |  | ||||||
|  | @ -80,6 +80,11 @@ public partial class MainWindow | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables); |             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.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).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.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); |             this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); | ||||||
|             this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); |             this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); | ||||||
|  |  | ||||||
|  | @ -1107,126 +1107,329 @@ | ||||||
|             </TabItem> |             </TabItem> | ||||||
| 
 | 
 | ||||||
|             <TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}"> |             <TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}"> | ||||||
|                 <Grid Margin="{StaticResource Margin8}"> |                 <ScrollViewer VerticalScrollBarVisibility="Visible"> | ||||||
|                     <Grid.RowDefinitions> |                     <Grid Margin="{StaticResource Margin8}"> | ||||||
|                         <RowDefinition Height="Auto" /> |                         <Grid.RowDefinitions> | ||||||
|                         <RowDefinition Height="Auto" /> |                             <RowDefinition Height="Auto" /> | ||||||
|                         <RowDefinition Height="Auto" /> |                         </Grid.RowDefinitions> | ||||||
|                         <RowDefinition Height="Auto" /> |                         <Grid.ColumnDefinitions> | ||||||
|                         <RowDefinition Height="Auto" /> |                             <ColumnDefinition Width="Auto" /> | ||||||
|                         <RowDefinition Height="Auto" /> |                             <ColumnDefinition Width="Auto" /> | ||||||
|                         <RowDefinition Height="Auto" /> |                         </Grid.ColumnDefinitions> | ||||||
|                         <RowDefinition Height="Auto" /> |                         <Grid | ||||||
|                     </Grid.RowDefinitions> |                             Grid.Row="0" | ||||||
|                     <Grid.ColumnDefinitions> |                             Grid.Column="0" | ||||||
|                         <ColumnDefinition Width="Auto" /> |                             Margin="{StaticResource Margin8}"> | ||||||
|                         <ColumnDefinition Width="Auto" /> |                             <Grid.RowDefinitions> | ||||||
|                     </Grid.ColumnDefinitions> |                                 <RowDefinition Height="Auto" /> | ||||||
|                     <TextBlock |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Grid.Row="1" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Grid.Column="0" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Margin="{StaticResource Margin8}" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         VerticalAlignment="Center" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Style="{StaticResource ToolbarTextBlock}" |                                 <RowDefinition Height="Auto" /> | ||||||
|                         Text="VMess" /> |                                 <RowDefinition Height="Auto" /> | ||||||
|                     <ComboBox |                             </Grid.RowDefinitions> | ||||||
|                         x:Name="cmbCoreType1" |                             <Grid.ColumnDefinitions> | ||||||
|                         Grid.Row="1" |                                 <ColumnDefinition Width="Auto" /> | ||||||
|                         Grid.Column="1" |                                 <ColumnDefinition Width="Auto" /> | ||||||
|                         Width="200" |                             </Grid.ColumnDefinitions> | ||||||
|                         Margin="{StaticResource Margin8}" |                             <TextBlock | ||||||
|                         Style="{StaticResource DefComboBox}" /> |                                 Grid.Row="1" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="VMess" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreType1" | ||||||
|  |                                 Grid.Row="1" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="2" |                                 Grid.Row="2" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Style="{StaticResource ToolbarTextBlock}" |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|                         Text="Custom" /> |                                 Text="Custom" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType2" |                                 x:Name="cmbCoreType2" | ||||||
|                         Grid.Row="2" |                                 Grid.Row="2" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         Style="{StaticResource DefComboBox}" /> |                                 Style="{StaticResource DefComboBox}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="3" |                                 Grid.Row="3" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Style="{StaticResource ToolbarTextBlock}" |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|                         Text="Shadowsocks" /> |                                 Text="Shadowsocks" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType3" |                                 x:Name="cmbCoreType3" | ||||||
|                         Grid.Row="3" |                                 Grid.Row="3" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         Style="{StaticResource DefComboBox}" /> |                                 Style="{StaticResource DefComboBox}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="4" |                                 Grid.Row="4" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Style="{StaticResource ToolbarTextBlock}" |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|                         Text="Socks" /> |                                 Text="Socks" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType4" |                                 x:Name="cmbCoreType4" | ||||||
|                         Grid.Row="4" |                                 Grid.Row="4" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         Style="{StaticResource DefComboBox}" /> |                                 Style="{StaticResource DefComboBox}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="5" |                                 Grid.Row="5" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Style="{StaticResource ToolbarTextBlock}" |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|                         Text="VLESS" /> |                                 Text="VLESS" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType5" |                                 x:Name="cmbCoreType5" | ||||||
|                         Grid.Row="5" |                                 Grid.Row="5" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         Style="{StaticResource DefComboBox}" /> |                                 Style="{StaticResource DefComboBox}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="6" |                                 Grid.Row="6" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Style="{StaticResource ToolbarTextBlock}" |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|                         Text="Trojan" /> |                                 Text="Trojan" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType6" |                                 x:Name="cmbCoreType6" | ||||||
|                         Grid.Row="6" |                                 Grid.Row="6" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         Style="{StaticResource DefComboBox}" /> |                                 Style="{StaticResource DefComboBox}" /> | ||||||
| 
 | 
 | ||||||
|                     <TextBlock |                             <TextBlock | ||||||
|                         Grid.Row="7" |                                 Grid.Row="7" | ||||||
|                         Grid.Column="0" |                                 Grid.Column="0" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         VerticalAlignment="Center" |                                 VerticalAlignment="Center" | ||||||
|                         Style="{StaticResource ToolbarTextBlock}" |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|                         Text="Wireguard" /> |                                 Text="Wireguard" /> | ||||||
|                     <ComboBox |                             <ComboBox | ||||||
|                         x:Name="cmbCoreType9" |                                 x:Name="cmbCoreType9" | ||||||
|                         Grid.Row="7" |                                 Grid.Row="7" | ||||||
|                         Grid.Column="1" |                                 Grid.Column="1" | ||||||
|                         Width="200" |                                 Width="200" | ||||||
|                         Margin="{StaticResource Margin8}" |                                 Margin="{StaticResource Margin8}" | ||||||
|                         Style="{StaticResource DefComboBox}" /> |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|                 </Grid> |                         </Grid> | ||||||
|  |                         <Grid | ||||||
|  |                             Grid.Row="0" | ||||||
|  |                             Grid.Column="1" | ||||||
|  |                             Margin="{StaticResource Margin8}"> | ||||||
|  |                             <Grid.RowDefinitions> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                                 <RowDefinition Height="Auto" /> | ||||||
|  |                             </Grid.RowDefinitions> | ||||||
|  |                             <Grid.ColumnDefinitions> | ||||||
|  |                                 <ColumnDefinition Width="Auto" /> | ||||||
|  |                                 <ColumnDefinition Width="Auto" /> | ||||||
|  |                             </Grid.ColumnDefinitions> | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="0" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Grid.ColumnSpan="2" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc1}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="1" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Grid.ColumnSpan="2" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc2}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="2" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="{x:Static resx:ResUI.TbSettingsSplitCoreEnable}" /> | ||||||
|  |                             <ToggleButton | ||||||
|  |                                 x:Name="togCoreSplit" | ||||||
|  |                                 Grid.Row="2" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 HorizontalAlignment="Left" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="3" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="路由 Core" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitRouteType" | ||||||
|  |                                 Grid.Row="3" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="4" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="VMess" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType1" | ||||||
|  |                                 Grid.Row="4" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="5" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="Shadowsocks" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType3" | ||||||
|  |                                 Grid.Row="5" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="6" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="Socks" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType4" | ||||||
|  |                                 Grid.Row="6" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="7" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="VLESS" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType5" | ||||||
|  |                                 Grid.Row="7" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="8" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="Trojan" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType6" | ||||||
|  |                                 Grid.Row="8" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="9" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="Hysteria2" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType7" | ||||||
|  |                                 Grid.Row="9" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="10" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="TUIC" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType8" | ||||||
|  |                                 Grid.Row="10" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  | 
 | ||||||
|  |                             <TextBlock | ||||||
|  |                                 Grid.Row="11" | ||||||
|  |                                 Grid.Column="0" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 VerticalAlignment="Center" | ||||||
|  |                                 Style="{StaticResource ToolbarTextBlock}" | ||||||
|  |                                 Text="Wireguard" /> | ||||||
|  |                             <ComboBox | ||||||
|  |                                 x:Name="cmbCoreSplitType9" | ||||||
|  |                                 Grid.Row="11" | ||||||
|  |                                 Grid.Column="1" | ||||||
|  |                                 Width="200" | ||||||
|  |                                 Margin="{StaticResource Margin8}" | ||||||
|  |                                 Style="{StaticResource DefComboBox}" /> | ||||||
|  |                         </Grid> | ||||||
|  |                     </Grid> | ||||||
|  |                 </ScrollViewer> | ||||||
|             </TabItem> |             </TabItem> | ||||||
|         </TabControl> |         </TabControl> | ||||||
|     </DockPanel> |     </DockPanel> | ||||||
|  |  | ||||||
|  | @ -43,6 +43,17 @@ public partial class OptionSettingWindow | ||||||
|         cmbCoreType6.ItemsSource = Global.CoreTypes; |         cmbCoreType6.ItemsSource = Global.CoreTypes; | ||||||
|         cmbCoreType9.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(); |         cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList(); | ||||||
|         cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); |         cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); | ||||||
|         cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls; |         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.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables); | ||||||
|             this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.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); |             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); | ||||||
|         }); |         }); | ||||||
|         WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); |         WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); | ||||||
|  |  | ||||||
|  | @ -122,7 +122,7 @@ public partial class RoutingSettingWindow | ||||||
| 
 | 
 | ||||||
|     private void linkdomainStrategy4Singbox_Click(object sender, RoutedEventArgs e) |     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) |     private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue