From 02a8fe0693715841252dde03a410ababec41c6bf Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 12 Apr 2026 01:06:41 +0800 Subject: [PATCH] Add sing-box send-through --- .../Singbox/CoreConfigSingboxService.cs | 4 ++ .../Singbox/SingboxConfigTemplateService.cs | 36 +++++++++++++++++ .../V2ray/CoreConfigV2rayService.cs | 39 ------------------- .../V2ray/V2rayConfigTemplateService.cs | 39 +++++++++++++++++++ v2rayN/v2rayN.slnx | 1 + 5 files changed, 80 insertions(+), 39 deletions(-) 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 @@ +