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;
}