mirror of
https://github.com/2dust/v2rayN.git
synced 2026-11-12 15:34:59 +03:00
Multi profile (#7929)
* Multi Profile * VM and wpf * avalonia * Fix right click not working * Exclude specific profile types from selection * Rename * Add Policy Group support * Add generate policy group * Adjust UI * Add Proxy Chain support * Fix * Add fallback support * Add PolicyGroup include other Group support * Add group in traffic splitting support * Avoid duplicate tags * Refactor * Adjust chained proxy, actual outbound is at the top Based on actual network flow instead of data packets * Add helper function * Refactor * Add chain selection control to group outbounds * Avoid self-reference * Fix * Improves Tun2Socks address handling * Avoids circular dependency in profile groups Adds cycle detection to prevent infinite loops when evaluating profile groups. This ensures that profile group configurations don't result in stack overflow errors when groups reference each other, directly or indirectly. * Fix * Fix * Update ProfileGroupItem.cs * Refactor * Remove unnecessary checks --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
This commit is contained in:
@@ -480,6 +480,58 @@ public partial class CoreConfigV2rayService
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenGroupOutbound(ProfileItem node, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (node.ConfigType is not (EConfigType.PolicyGroup or EConfigType.ProxyChain))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
||||
if (hasCycle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
switch (node.ConfigType)
|
||||
{
|
||||
case EConfigType.PolicyGroup:
|
||||
if (ignoreOriginChain)
|
||||
{
|
||||
await GenOutboundsList(childProfiles, v2rayConfig, baseTagName);
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenOutboundsListWithChain(childProfiles, v2rayConfig, baseTagName);
|
||||
}
|
||||
break;
|
||||
case EConfigType.ProxyChain:
|
||||
await GenChainOutboundsList(childProfiles, v2rayConfig, baseTagName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//add balancers
|
||||
if (node.ConfigType == EConfigType.PolicyGroup)
|
||||
{
|
||||
await GenObservatory(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||
await GenBalancer(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
//fragment proxy
|
||||
@@ -552,7 +604,7 @@ public partial class CoreConfigV2rayService
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig)
|
||||
private async Task<int> GenOutboundsListWithChain(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -577,6 +629,25 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
index++;
|
||||
|
||||
if (node.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
|
||||
{
|
||||
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var childBaseTagName = $"{baseTagName}-{index}";
|
||||
var ret = node.ConfigType switch
|
||||
{
|
||||
EConfigType.PolicyGroup =>
|
||||
await GenOutboundsListWithChain(childProfiles, v2rayConfig, childBaseTagName),
|
||||
EConfigType.ProxyChain =>
|
||||
await GenChainOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
@@ -590,7 +661,7 @@ public partial class CoreConfigV2rayService
|
||||
|
||||
// current proxy
|
||||
await GenOutbound(node, currentOutbound);
|
||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
currentOutbound.tag = $"{baseTagName}-{index}";
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
@@ -606,7 +677,7 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevTag = $"prev-{baseTagName}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
@@ -628,9 +699,17 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
|
||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
||||
resultOutbounds.AddRange(prevOutbounds);
|
||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||
v2rayConfig.outbounds = resultOutbounds;
|
||||
if (baseTagName == Global.ProxyTag)
|
||||
{
|
||||
resultOutbounds.AddRange(prevOutbounds);
|
||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||
v2rayConfig.outbounds = resultOutbounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
v2rayConfig.outbounds.AddRange(prevOutbounds);
|
||||
v2rayConfig.outbounds.AddRange(resultOutbounds);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -692,4 +771,110 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
||||
{
|
||||
var resultOutbounds = new List<Outbounds4Ray>();
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var node = nodes[i];
|
||||
if (node == null)
|
||||
continue;
|
||||
if (node.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
|
||||
{
|
||||
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var childBaseTagName = $"{baseTagName}-{i + 1}";
|
||||
var ret = node.ConfigType switch
|
||||
{
|
||||
EConfigType.PolicyGroup =>
|
||||
await GenOutboundsListWithChain(childProfiles, v2rayConfig, childBaseTagName),
|
||||
EConfigType.ProxyChain =>
|
||||
await GenChainOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
continue;
|
||||
}
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
if (txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
var result = await GenOutbound(node, outbound);
|
||||
if (result != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
outbound.tag = baseTagName + (i + 1).ToString();
|
||||
resultOutbounds.Add(outbound);
|
||||
}
|
||||
if (baseTagName == Global.ProxyTag)
|
||||
{
|
||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||
v2rayConfig.outbounds = resultOutbounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
v2rayConfig.outbounds.AddRange(resultOutbounds);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenChainOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
||||
{
|
||||
// Based on actual network flow instead of data packets
|
||||
var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
|
||||
var resultOutbounds = new List<Outbounds4Ray>();
|
||||
for (var i = 0; i < nodesReverse.Count; i++)
|
||||
{
|
||||
var node = nodesReverse[i];
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
if (txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
var result = await GenOutbound(node, outbound);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
outbound.tag = baseTagName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// avoid v2ray observe
|
||||
outbound.tag = "chain-" + baseTagName + i.ToString();
|
||||
}
|
||||
|
||||
if (i != nodesReverse.Count - 1)
|
||||
{
|
||||
outbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = "chain-" + baseTagName + (i + 1).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
resultOutbounds.Add(outbound);
|
||||
}
|
||||
if (baseTagName == Global.ProxyTag)
|
||||
{
|
||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||
v2rayConfig.outbounds = resultOutbounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
v2rayConfig.outbounds.AddRange(resultOutbounds);
|
||||
}
|
||||
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user