From 2126ea5a87055514a6ea68b7efae099b949720ea Mon Sep 17 00:00:00 2001 From: DHR60 <192860629+DHR60@users.noreply.github.com> Date: Thu, 18 Jun 2026 02:33:58 +0000 Subject: [PATCH] Fix tun (#9582) * Fix tun * Format --- .../Builder/CoreConfigContextBuilder.cs | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs index b7481e1a..e003adee 100644 --- a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs +++ b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs @@ -6,8 +6,8 @@ public record CoreConfigContextBuilderResult(CoreConfigContext Context, NodeVali } /// -/// 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). +/// 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, @@ -17,8 +17,8 @@ public record CoreConfigContextBuilderAllResult( public bool Success => MainResult.Success && (PreSocksResult?.Success ?? true); /// - /// Merges all errors and warnings from the main result and the optional pre-socks result - /// into a single for unified notification. + /// Merges all errors and warnings from the main result and the optional pre-socks result + /// into a single for unified notification. /// public NodeValidatorResult CombinedValidatorResult => new( [.. MainResult.ValidatorResult.Errors, .. PreSocksResult?.ValidatorResult.Errors ?? []], @@ -28,14 +28,14 @@ public record CoreConfigContextBuilderAllResult( public class CoreConfigContextBuilder { /// - /// Builds a for the given node, resolves its proxy map, - /// and processes outbound nodes referenced by routing rules. + /// Builds a for the given node, resolves its proxy map, + /// and processes outbound nodes referenced by routing rules. /// public static async Task Build(Config config, ProfileItem node) { var runCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType); var coreType = runCoreType == ECoreType.sing_box ? ECoreType.sing_box : ECoreType.Xray; - var context = new CoreConfigContext() + var context = new CoreConfigContext { Node = node, RunCoreType = runCoreType, @@ -61,7 +61,8 @@ public class CoreConfigContextBuilder if (!(context.RoutingItem?.RuleSet.IsNullOrEmpty() ?? true)) { var rules = JsonUtils.Deserialize>(context.RoutingItem?.RuleSet) ?? []; - foreach (var ruleItem in rules.Where(ruleItem => ruleItem.Enabled && !Global.OutboundTags.Contains(ruleItem.OutboundTag))) + foreach (var ruleItem in rules.Where(ruleItem => + ruleItem.Enabled && !Global.OutboundTags.Contains(ruleItem.OutboundTag))) { if (ruleItem.OutboundTag.IsNullOrEmpty()) { @@ -72,7 +73,8 @@ public class CoreConfigContextBuilder var ruleOutboundNode = await AppManager.Instance.GetProfileItemViaRemarks(ruleItem.OutboundTag); if (ruleOutboundNode == null) { - validatorResult.Warnings.Add(string.Format(ResUI.MsgRoutingRuleOutboundNodeNotFound, ruleItem.Remarks, ruleItem.OutboundTag)); + validatorResult.Warnings.Add(string.Format(ResUI.MsgRoutingRuleOutboundNodeNotFound, + ruleItem.Remarks, ruleItem.OutboundTag)); ruleItem.OutboundTag = Global.ProxyTag; continue; } @@ -83,7 +85,8 @@ public class CoreConfigContextBuilder if (!ruleNodeValidatorResult.Success) { validatorResult.Warnings.AddRange(ruleNodeValidatorResult.Errors.Select(e => - string.Format(ResUI.MsgRoutingRuleOutboundNodeError, ruleItem.Remarks, ruleItem.OutboundTag, e))); + string.Format(ResUI.MsgRoutingRuleOutboundNodeError, ruleItem.Remarks, ruleItem.OutboundTag, + e))); ruleItem.OutboundTag = Global.ProxyTag; continue; } @@ -110,9 +113,9 @@ public class CoreConfigContextBuilder } /// - /// 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. + /// 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) { @@ -132,16 +135,31 @@ public class CoreConfigContextBuilder { Context = mainResult.Context with { - IsTunEnabled = false, // main core doesn't handle tun directly when pre-socks is used + IsTunEnabled = false, + // main core doesn't handle tun directly when pre-socks is used ProtectDomainList = [.. mainResult.Context.ProtectDomainList, .. preResult.Context.ProtectDomainList], - } + }, }; + if (mainResult.Context.IsTunEnabled + && mainResult.Context.AppConfig.TunModeItem.StrictRoute) + { + var appConfig = JsonUtils.DeepCopy(mainResult.Context.AppConfig); + appConfig.CoreBasicItem.BindInterface = string.Empty; + appConfig.CoreBasicItem.SendThrough = string.Empty; + resolvedMainResult = resolvedMainResult with + { + Context = resolvedMainResult.Context with + { + AppConfig = appConfig, + }, + }; + } return new CoreConfigContextBuilderAllResult(resolvedMainResult, 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. + /// 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) { @@ -157,8 +175,9 @@ public class CoreConfigContextBuilder { Context = preSocksResult.Context with { - ProtectDomainList = [.. nodeContext.ProtectDomainList ?? [], .. preSocksResult.Context.ProtectDomainList ?? []], - } + ProtectDomainList = + [.. nodeContext.ProtectDomainList ?? [], .. preSocksResult.Context.ProtectDomainList ?? []], + }, }; } @@ -166,8 +185,8 @@ public class CoreConfigContextBuilder } /// - /// 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. + /// 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. /// public static async Task<(ProfileItem, NodeValidatorResult)> ResolveNodeAsync(CoreConfigContext context, ProfileItem node, @@ -202,12 +221,13 @@ public class CoreConfigContextBuilder } /// - /// If the node's subscription defines prev/next profiles, creates a virtual - /// node that wraps them together. - /// Returns null as the chain item when no chain is needed. - /// Any warnings (e.g. missing prev/next profile) are returned in the validator result. + /// If the node's subscription defines prev/next profiles, creates a virtual + /// node that wraps them together. + /// Returns null as the chain item when no chain is needed. + /// Any warnings (e.g. missing prev/next profile) are returned in the validator result. /// - private static async Task<(ProfileItem? ChainNode, NodeValidatorResult ValidatorResult)> BuildSubscriptionChainNodeAsync(ProfileItem node) + private static async Task<(ProfileItem? ChainNode, NodeValidatorResult ValidatorResult)> + BuildSubscriptionChainNodeAsync(ProfileItem node) { var result = NodeValidatorResult.Empty(); @@ -248,7 +268,7 @@ public class CoreConfigContextBuilder } // Build new proxy chain node - var chainNode = new ProfileItem() + var chainNode = new ProfileItem { IndexId = $"inner-{Utils.GetGuid(false)}", ConfigType = EConfigType.ProxyChain, @@ -266,8 +286,8 @@ public class CoreConfigContextBuilder } /// - /// Dispatches registration to either or - /// based on the node's config type. + /// Dispatches registration to either or + /// based on the node's config type. /// private static async Task RegisterNodeAsync(CoreConfigContext context, ProfileItem node) { @@ -275,15 +295,12 @@ public class CoreConfigContextBuilder { return await RegisterGroupNodeAsync(context, node); } - else - { - return RegisterSingleNodeAsync(context, node); - } + return RegisterSingleNodeAsync(context, node); } /// - /// Validates a single (non-group) node and, on success, adds it to the proxy map - /// and records any domain addresses that should bypass the proxy. + /// Validates a single (non-group) node and, on success, adds it to the proxy map + /// and records any domain addresses that should bypass the proxy. /// private static NodeValidatorResult RegisterSingleNodeAsync(CoreConfigContext context, ProfileItem node) { @@ -347,8 +364,8 @@ public class CoreConfigContextBuilder } /// - /// Entry point for registering a group node. Initialises the visited/ancestor sets - /// and delegates to . + /// Entry point for registering a group node. Initialises the visited/ancestor sets + /// and delegates to . /// private static async Task RegisterGroupNodeAsync(CoreConfigContext context, ProfileItem node) @@ -364,9 +381,9 @@ public class CoreConfigContextBuilder } /// - /// Recursively walks the children of a group node, registering valid leaf nodes - /// and nested groups. Detects cycles via and - /// deduplicates shared nodes via . + /// Recursively walks the children of a group node, registering valid leaf nodes + /// and nested groups. Detects cycles via and + /// deduplicates shared nodes via . /// private static async Task TraverseGroupNodeAsync( CoreConfigContext context, @@ -430,13 +447,10 @@ public class CoreConfigContextBuilder childNodeValidatorResult.Errors.Add(string.Format(ResUI.MsgGroupNoValidChildNode, node.Remarks)); return childNodeValidatorResult; } - else - { - childNodeValidatorResult.Warnings.AddRange(childNodeValidatorResult.Errors); - childNodeValidatorResult.Errors.Clear(); - } + childNodeValidatorResult.Warnings.AddRange(childNodeValidatorResult.Errors); + childNodeValidatorResult.Errors.Clear(); - node.SetProtocolExtra(node.GetProtocolExtra() with { ChildItems = Utils.List2String(childIndexIdList), }); + node.SetProtocolExtra(node.GetProtocolExtra() with { ChildItems = Utils.List2String(childIndexIdList) }); context.AllProxiesMap[node.IndexId] = node; return childNodeValidatorResult; }