From babf97934279af7e0010af7aecc97eb01c3a8248 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 20 Jun 2025 11:55:43 +0800 Subject: [PATCH] Improves sing-box outbound proxy chain handling. --- .../CoreConfig/CoreConfigSingboxService.cs | 140 ++++++++++++++---- 1 file changed, 112 insertions(+), 28 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 27ed39b9..b6d37abb 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -336,7 +336,7 @@ public class CoreConfigSingboxService await GenExperimental(singboxConfig); singboxConfig.outbounds.RemoveAt(0); - var tagProxy = new List(); + var proxyProfiles = new List(); foreach (var it in selecteds) { if (it.ConfigType == EConfigType.Custom) @@ -370,42 +370,18 @@ public class CoreConfigSingboxService } //outbound - var outbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(item, outbound); - outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}"; - singboxConfig.outbounds.Insert(0, outbound); - tagProxy.Add(outbound.tag); + proxyProfiles.Add(item); } - if (tagProxy.Count <= 0) + if (proxyProfiles.Count <= 0) { ret.Msg = ResUI.FailedGenDefaultConfiguration; return ret; } + await GenOutboundsList(proxyProfiles, singboxConfig); await GenDns(null, singboxConfig); await ConvertGeo2Ruleset(singboxConfig); - //add urltest outbound - var outUrltest = new Outbound4Sbox - { - type = "urltest", - tag = $"{Global.ProxyTag}-auto", - outbounds = tagProxy, - interrupt_exist_connections = false, - }; - singboxConfig.outbounds.Insert(0, outUrltest); - - //add selector outbound - var outSelector = new Outbound4Sbox - { - type = "selector", - tag = Global.ProxyTag, - outbounds = JsonUtils.DeepCopy(tagProxy), - interrupt_exist_connections = false, - }; - outSelector.outbounds.Insert(0, outUrltest.tag); - singboxConfig.outbounds.Insert(0, outSelector); - ret.Success = true; ret.Data = JsonUtils.Serialize(singboxConfig); return ret; @@ -974,6 +950,114 @@ public class CoreConfigSingboxService return 0; } + private async Task GenOutboundsList(List nodes, SingboxConfig singboxConfig) + { + try + { + var proxyOutbounds = new List(); + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + var chainProxy = new Dictionary(); + var proxyTags = new List(); + + var index = 0; + + foreach (var node in nodes) + { + index++; + Outbound4Sbox? prevOutbound = null; + Outbound4Sbox? nextOutbound = null; + if (node.Subid.IsNotEmpty()) + { + var subItem = await AppHandler.Instance.GetSubItem(node.Subid); + if (chainProxy.ContainsKey(node.Subid)) + { + (prevOutbound, nextOutbound) = chainProxy[node.Subid]; + } + else if (subItem is not null) + { + var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); + if (prevNode is not null + && prevNode.ConfigType != EConfigType.Custom) + { + prevOutbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(prevNode, prevOutbound); + } + + var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); + if (nextNode is not null + && nextNode.ConfigType != EConfigType.Custom) + { + nextOutbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(nextNode, nextOutbound); + } + + chainProxy.TryAdd(node.Subid, (prevOutbound, nextOutbound)); + + if (prevOutbound is not null) + { + prevOutbound.tag = $"1-{Global.ProxyTag}-{index}"; + proxyOutbounds.Add(prevOutbound); + } + } + } + if (node.ConfigType is EConfigType.Custom) + { + continue; + } + var outbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(node, outbound); + outbound.tag = $"{Global.ProxyTag}-{index}"; + proxyTags.Add(outbound.tag); + proxyOutbounds.Add(outbound); + + if (prevOutbound is not null) + { + outbound.detour = prevOutbound.tag; + } + + if (nextOutbound is not null) + { + var nextOutboundCopy = JsonUtils.DeepCopy(nextOutbound); + + nextOutboundCopy.tag = outbound.tag; + outbound.tag = $"3-{Global.ProxyTag}-{index}"; + nextOutboundCopy.detour = outbound.tag; + proxyOutbounds.Add(nextOutboundCopy); + } + } + + //add urltest outbound + var outUrltest = new Outbound4Sbox + { + type = "urltest", + tag = $"{Global.ProxyTag}-auto", + outbounds = proxyTags, + interrupt_exist_connections = false, + }; + proxyOutbounds.Insert(0, outUrltest); + + //add selector outbound + var outSelector = new Outbound4Sbox + { + type = "selector", + tag = Global.ProxyTag, + outbounds = JsonUtils.DeepCopy(proxyTags), + interrupt_exist_connections = false, + }; + outSelector.outbounds.Insert(0, outUrltest.tag); + proxyOutbounds.Insert(0, outSelector); + + proxyOutbounds.AddRange(singboxConfig.outbounds); + singboxConfig.outbounds = proxyOutbounds; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + + return 0; + } + private async Task GenRouting(SingboxConfig singboxConfig) { try