From 624728dbe75b420ebba224c3d754f7992a4855a0 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 1 Mar 2026 10:54:18 +0800 Subject: [PATCH] Build all contexts --- .../Builder/CoreConfigContextBuilder.cs | 98 +++++++++++++++++++ v2rayN/ServiceLib/Handler/ConfigHandler.cs | 56 ----------- v2rayN/ServiceLib/Manager/CoreManager.cs | 40 ++------ .../ViewModels/MainWindowViewModel.cs | 17 ++-- 4 files changed, 116 insertions(+), 95 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs index 23a495f8..0139d3fe 100644 --- a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs +++ b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs @@ -5,6 +5,30 @@ public record CoreConfigContextBuilderResult(CoreConfigContext Context, NodeVali public bool Success => ValidatorResult.Success; } +/// +/// Holds the results of a full context build, including the main context and an optional +/// pre-socks context (e.g. for TUN protection or pre-socks chaining). +/// +public record CoreConfigContextBuilderAllResult( + CoreConfigContextBuilderResult MainResult, + CoreConfigContextBuilderResult? PreSocksResult) +{ + /// True only when both the main result and (if present) the pre-socks result succeeded. + public bool Success => MainResult.Success && (PreSocksResult?.Success ?? true); + + /// + /// The main context with TunProtectSsPort/ProxyRelaySsPort merged in from the + /// pre-socks result (if any). Pass this to the core runner. + /// + public CoreConfigContext ResolvedMainContext => PreSocksResult is not null + ? MainResult.Context with + { + TunProtectSsPort = PreSocksResult.Context.TunProtectSsPort, + ProxyRelaySsPort = PreSocksResult.Context.ProxyRelaySsPort, + } + : MainResult.Context; +} + public class CoreConfigContextBuilder { /// @@ -75,6 +99,80 @@ public class CoreConfigContextBuilder return new CoreConfigContextBuilderResult(context, validatorResult); } + /// + /// Builds the main for and, when + /// the main build succeeds, also builds the optional pre-socks context required for TUN + /// protection or pre-socks proxy chaining. + /// + public static async Task BuildAll(Config config, ProfileItem node) + { + var mainResult = await Build(config, node); + if (!mainResult.Success) + { + return new CoreConfigContextBuilderAllResult(mainResult, null); + } + + var preResult = await BuildPreSocksIfNeeded(mainResult.Context); + return new CoreConfigContextBuilderAllResult(mainResult, preResult); + } + + /// + /// Determines whether a pre-socks context is required for + /// and, if so, builds and returns it. Returns null when no pre-socks core is needed. + /// + private static async Task BuildPreSocksIfNeeded(CoreConfigContext nodeContext) + { + var config = nodeContext.AppConfig; + var node = nodeContext.Node; + var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); + + var preSocksItem = ConfigHandler.GetPreSocksItem(config, node, coreType); + if (preSocksItem != null) + { + var preSocksResult = await Build(nodeContext.AppConfig, preSocksItem); + var protectDomainList = nodeContext.ProtectDomainList ?? []; + protectDomainList.UnionWith(preSocksResult.Context.ProtectDomainList ?? []); + return preSocksResult with + { + Context = preSocksResult.Context with { ProtectDomainList = protectDomainList } + }; + } + + if (!nodeContext.IsTunEnabled + || coreType != ECoreType.Xray + || node.ConfigType == EConfigType.Custom) + { + return null; + } + + var tunProtectSsPort = Utils.GetFreePort(); + var proxyRelaySsPort = Utils.GetFreePort(); + var preItem = new ProfileItem() + { + CoreType = ECoreType.sing_box, + ConfigType = EConfigType.Shadowsocks, + Address = Global.Loopback, + Port = proxyRelaySsPort, + Password = Global.None, + }; + preItem.SetProtocolExtra(preItem.GetProtocolExtra() with + { + SsMethod = Global.None, + }); + var preResult2 = await Build(nodeContext.AppConfig, preItem); + var protectDomainList2 = nodeContext.ProtectDomainList ?? []; + protectDomainList2.UnionWith(preResult2.Context.ProtectDomainList ?? []); + return preResult2 with + { + Context = preResult2.Context with + { + ProtectDomainList = protectDomainList2, + TunProtectSsPort = tunProtectSsPort, + ProxyRelaySsPort = proxyRelaySsPort, + } + }; + } + /// /// Resolves a node into the context, optionally wrapping it in a subscription-level proxy chain. /// Returns the effective (possibly replaced) node and the validation result. diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index faa35650..a3bd3f8c 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1251,62 +1251,6 @@ public static class ConfigHandler return itemSocks; } - public static async Task GetPreSocksCoreConfigContext(CoreConfigContext nodeContext) - { - var config = nodeContext.AppConfig; - var node = nodeContext.Node; - var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); - - var preSocksItem = GetPreSocksItem(config, node, coreType); - if (preSocksItem != null) - { - var preSocksResult = await CoreConfigContextBuilder.Build(nodeContext.AppConfig, preSocksItem); - // share protect domain - var protectDomainList = nodeContext.ProtectDomainList ?? []; - protectDomainList.UnionWith(preSocksResult.Context.ProtectDomainList ?? []); - preSocksResult = preSocksResult with - { - Context = preSocksResult.Context with { ProtectDomainList = protectDomainList, } - }; - return preSocksResult; - } - - if ((!nodeContext.IsTunEnabled) - || coreType != ECoreType.Xray - || node.ConfigType == EConfigType.Custom) - { - return null; - } - var tunProtectSsPort = Utils.GetFreePort(); - var proxyRelaySsPort = Utils.GetFreePort(); - var preItem = new ProfileItem() - { - CoreType = ECoreType.sing_box, - ConfigType = EConfigType.Shadowsocks, - Address = Global.Loopback, - Port = proxyRelaySsPort, - Password = Global.None, - }; - preItem.SetProtocolExtra(preItem.GetProtocolExtra() with - { - SsMethod = Global.None, - }); - var preResult = await CoreConfigContextBuilder.Build(nodeContext.AppConfig, preItem); - // share protect domain - var protectDomainList2 = nodeContext.ProtectDomainList ?? []; - protectDomainList2.UnionWith(preResult.Context.ProtectDomainList ?? []); - preResult = preResult with - { - Context = preResult.Context with - { - ProtectDomainList = protectDomainList2, - TunProtectSsPort = tunProtectSsPort, - ProxyRelaySsPort = proxyRelaySsPort, - } - }; - return preResult; - } - /// /// Remove servers with invalid test results (timeout) /// Useful for cleaning up subscription lists diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs index 83355597..b2df4562 100644 --- a/v2rayN/ServiceLib/Manager/CoreManager.cs +++ b/v2rayN/ServiceLib/Manager/CoreManager.cs @@ -57,45 +57,19 @@ public class CoreManager } } - public async Task LoadCore(CoreConfigContext? context) + /// Resolved main context (with pre-socks ports already merged if applicable). + /// Optional pre-socks context passed to . + public async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext) { - if (context == null) + if (mainContext == null) { await UpdateFunc(false, ResUI.CheckServerSettings); return; } - var contextMod = context; - var node = contextMod.Node; + var node = mainContext.Node; var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); - var preResult = await ConfigHandler.GetPreSocksCoreConfigContext(contextMod); - if (preResult is not null) - { - var validatorResult = preResult.ValidatorResult; - var msgs = new List([.. validatorResult.Errors, .. validatorResult.Warnings]); - if (msgs.Count > 0) - { - foreach (var msg in msgs) - { - await UpdateFunc(false, msg); - } - await UpdateFunc(true, Utils.List2String(msgs.Take(10).ToList(), true)); - if (!validatorResult.Success) - { - return; - } - } - } - var preContext = preResult?.Context; - if (preContext is not null) - { - contextMod = contextMod with - { - TunProtectSsPort = preContext.TunProtectSsPort, - ProxyRelaySsPort = preContext.ProxyRelaySsPort, - }; - } - var result = await CoreConfigHandler.GenerateClientConfig(contextMod, fileName); + var result = await CoreConfigHandler.GenerateClientConfig(mainContext, fileName); if (result.Success != true) { await UpdateFunc(true, result.Msg); @@ -114,7 +88,7 @@ public class CoreManager await WindowsUtils.RemoveTunDevice(); } - await CoreStart(contextMod); + await CoreStart(mainContext); await CoreStartPreService(preContext); if (_processService != null) { diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index becabf25..fedfbe9d 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -546,8 +546,13 @@ public class MainWindowViewModel : MyReactiveObject NoticeManager.Instance.Enqueue(ResUI.CheckServerSettings); return; } - var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, profileItem); - var msgs = new List([..validatorResult.Errors, ..validatorResult.Warnings]); + var allResult = await CoreConfigContextBuilder.BuildAll(_config, profileItem); + var msgs = new List([.. allResult.MainResult.ValidatorResult.Errors, .. allResult.MainResult.ValidatorResult.Warnings]); + if (allResult.PreSocksResult is not null) + { + msgs.AddRange(allResult.PreSocksResult.ValidatorResult.Errors); + msgs.AddRange(allResult.PreSocksResult.ValidatorResult.Warnings); + } if (msgs.Count > 0) { foreach (var msg in msgs) @@ -555,7 +560,7 @@ public class MainWindowViewModel : MyReactiveObject NoticeManager.Instance.SendMessage(msg); } NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true)); - if (!validatorResult.Success) + if (!allResult.Success) { return; } @@ -563,7 +568,7 @@ public class MainWindowViewModel : MyReactiveObject await Task.Run(async () => { - await LoadCore(context); + await LoadCore(allResult.ResolvedMainContext, allResult.PreSocksResult?.Context); await SysProxyHandler.UpdateSysProxy(_config, false); await Task.Delay(1000); }); @@ -604,9 +609,9 @@ public class MainWindowViewModel : MyReactiveObject RxApp.MainThreadScheduler.Schedule(() => BlReloadEnabled = enabled); } - private async Task LoadCore(CoreConfigContext? context) + private async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext) { - await CoreManager.Instance.LoadCore(context); + await CoreManager.Instance.LoadCore(mainContext, preContext); } #endregion core job