From 6b07ca63a0a87da3b431879be224631a5b80e4cb Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 12 Apr 2026 01:25:17 +0000 Subject: [PATCH] Add sing-box send-through (#9081) * Add sing-box send-through * Perf resx --- v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 36 ++++++++--------- v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.fr.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 4 +- v2rayN/ServiceLib/Resx/ResUI.ru.resx | 5 ++- v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 4 +- v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 ++ .../Singbox/CoreConfigSingboxService.cs | 4 ++ .../Singbox/SingboxConfigTemplateService.cs | 36 +++++++++++++++++ .../V2ray/CoreConfigV2rayService.cs | 39 ------------------- .../V2ray/V2rayConfigTemplateService.cs | 39 +++++++++++++++++++ v2rayN/v2rayN.slnx | 1 + 13 files changed, 118 insertions(+), 62 deletions(-) diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 81b46ac6..c64b7402 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -4014,24 +4014,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 Local outbound address (SendThrough) 的本地化字符串。 - /// - public static string TbSettingsSendThrough { - get { - return ResourceManager.GetString("TbSettingsSendThrough", resourceCulture); - } - } - - /// - /// 查找类似 Only applies to Xray. Fill in a local IPv4 address; leave empty to disable. 的本地化字符串。 - /// - public static string TbSettingsSendThroughTip { - get { - return ResourceManager.GetString("TbSettingsSendThroughTip", resourceCulture); - } - } - /// /// 查找类似 Enable Log 的本地化字符串。 /// @@ -4176,6 +4158,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Local outbound address (SendThrough) 的本地化字符串。 + /// + public static string TbSettingsSendThrough { + get { + return ResourceManager.GetString("TbSettingsSendThrough", resourceCulture); + } + } + + /// + /// 查找类似 For multi-interface environments, enter the local machine's IPv4 address 的本地化字符串。 + /// + public static string TbSettingsSendThroughTip { + get { + return ResourceManager.GetString("TbSettingsSendThroughTip", resourceCulture); + } + } + /// /// 查找类似 Set Win10 UWP Loopback 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 61ccbf7b..56e7bd5c 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1698,4 +1698,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Legacy TUN Protect + + For multi-interface environments, enter the local machine's IPv4 address + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index d466eedd..9e81a7c9 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1695,4 +1695,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Legacy TUN Protect + + For multi-interface environments, enter the local machine's IPv4 address + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 89456f99..8f64e04b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1698,4 +1698,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Legacy TUN Protect + + For multi-interface environments, enter the local machine's IPv4 address + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index fbab6252..3b21ec73 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1324,7 +1324,7 @@ Local outbound address (SendThrough) - Only applies to Xray. Fill in a local IPv4 address; leave empty to disable. + For multi-interface environments, enter the local machine's IPv4 address Please fill in the correct IPv4 address for SendThrough. @@ -1707,4 +1707,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Legacy TUN Protect - + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 9c6ff77e..86a613c3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1698,4 +1698,7 @@ Устаревшая защита TUN (Legacy Protect) - + + For multi-interface environments, enter the local machine's IPv4 address + + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 8416287e..36e5efc0 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1321,7 +1321,7 @@ 本地出站地址 (SendThrough) - 仅对 Xray 生效,填写本机 IPv4;留空则不设置。 + 用于多网口环境,请填写本机 IPv4 地址 请填写正确的 SendThrough IPv4 地址。 @@ -1704,4 +1704,4 @@ 旧版 TUN 保护 - + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 59a9fc9f..be707759 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1695,4 +1695,7 @@ Legacy TUN Protect + + For multi-interface environments, enter the local machine's IPv4 address + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs index 4f0203af..f0a4f0ef 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs @@ -57,6 +57,8 @@ public partial class CoreConfigSingboxService(CoreConfigContext context) ConvertGeo2Ruleset(); + ApplyOutboundSendThrough(); + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Success = true; @@ -221,6 +223,7 @@ public partial class CoreConfigSingboxService(CoreConfigContext context) _coreConfig.route.rules.Add(rule); } + ApplyOutboundSendThrough(); ret.Success = true; ret.Data = JsonUtils.Serialize(_coreConfig); return ret; @@ -279,6 +282,7 @@ public partial class CoreConfigSingboxService(CoreConfigContext context) listen_port = port, type = EInboundProtocol.mixed.ToString(), }); + ApplyOutboundSendThrough(); ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Success = true; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxConfigTemplateService.cs index 25b29701..d20aec1c 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxConfigTemplateService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxConfigTemplateService.cs @@ -58,4 +58,40 @@ public partial class CoreConfigSingboxService return JsonUtils.Serialize(fullConfigTemplateNode); } + + private void ApplyOutboundSendThrough() + { + var sendThrough = _config.CoreBasicItem.SendThrough?.TrimEx(); + foreach (var outbound in _coreConfig.outbounds ?? []) + { + outbound.inet4_bind_address = ShouldApplySendThrough(outbound, sendThrough) ? sendThrough : null; + } + } + + private static bool ShouldApplySendThrough(Outbound4Sbox outbound, string? sendThrough) + { + if (sendThrough.IsNullOrEmpty()) + { + return false; + } + + if (outbound.type is "direct" or "block" or "dns" or "selector" or "urltest") + { + return false; + } + + if (!outbound.detour.IsNullOrEmpty()) + { + return false; + } + + var outboundAddress = outbound.server ?? string.Empty; + + if (outboundAddress.Equals("localhost", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return !IPAddress.TryParse(outboundAddress, out var address) || !IPAddress.IsLoopback(address); + } } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs index 1e72de41..fb8d92a7 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -407,43 +407,4 @@ public partial class CoreConfigV2rayService(CoreConfigContext context) } #endregion public gen function - - private void ApplyOutboundSendThrough() - { - var sendThrough = _config.CoreBasicItem.SendThrough?.TrimEx(); - foreach (var outbound in _coreConfig.outbounds ?? []) - { - outbound.sendThrough = ShouldApplySendThrough(outbound, sendThrough) ? sendThrough : null; - } - } - - private static bool ShouldApplySendThrough(Outbounds4Ray outbound, string? sendThrough) - { - if (sendThrough.IsNullOrEmpty()) - { - return false; - } - - if (outbound.protocol is "freedom" or "blackhole" or "dns" or "loopback") - { - return false; - } - - if (outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == false) - { - return false; - } - - var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address - ?? outbound.settings?.vnext?.FirstOrDefault()?.address - ?? outbound.settings?.address?.ToString() - ?? string.Empty; - - if (outboundAddress.Equals("localhost", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return !IPAddress.TryParse(outboundAddress, out var address) || !IPAddress.IsLoopback(address); - } } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs index db7c3f32..6e563f0f 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs @@ -127,4 +127,43 @@ public partial class CoreConfigV2rayService return JsonUtils.Serialize(fullConfigTemplateNode); } + + private void ApplyOutboundSendThrough() + { + var sendThrough = _config.CoreBasicItem.SendThrough?.TrimEx(); + foreach (var outbound in _coreConfig.outbounds ?? []) + { + outbound.sendThrough = ShouldApplySendThrough(outbound, sendThrough) ? sendThrough : null; + } + } + + private static bool ShouldApplySendThrough(Outbounds4Ray outbound, string? sendThrough) + { + if (sendThrough.IsNullOrEmpty()) + { + return false; + } + + if (outbound.protocol is "freedom" or "blackhole" or "dns" or "loopback") + { + return false; + } + + if (outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == false) + { + return false; + } + + var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address + ?? outbound.settings?.vnext?.FirstOrDefault()?.address + ?? outbound.settings?.address?.ToString() + ?? string.Empty; + + if (outboundAddress.Equals("localhost", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return !IPAddress.TryParse(outboundAddress, out var address) || !IPAddress.IsLoopback(address); + } } diff --git a/v2rayN/v2rayN.slnx b/v2rayN/v2rayN.slnx index 1550478b..4042dad1 100644 --- a/v2rayN/v2rayN.slnx +++ b/v2rayN/v2rayN.slnx @@ -16,6 +16,7 @@ +