mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-28 13:13:04 +00:00
Compare commits
6 commits
2cd4be95a3
...
bfbde8268a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfbde8268a | ||
|
|
f82eddd35f | ||
|
|
eec3bb71cb | ||
|
|
7b31bcdd9f | ||
|
|
aea7078e95 | ||
|
|
9c82df5b49 |
46 changed files with 1456 additions and 2244 deletions
|
|
@ -15,7 +15,6 @@ public class Global
|
||||||
public const string CoreConfigFileName = "config.json";
|
public const string CoreConfigFileName = "config.json";
|
||||||
public const string CorePreConfigFileName = "configPre.json";
|
public const string CorePreConfigFileName = "configPre.json";
|
||||||
public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
|
public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
|
||||||
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
|
|
||||||
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
||||||
|
|
||||||
public const string NamespaceSample = "ServiceLib.Sample.";
|
public const string NamespaceSample = "ServiceLib.Sample.";
|
||||||
|
|
@ -88,7 +87,6 @@ public class Global
|
||||||
public const string SingboxLocalDNSTag = "local_local";
|
public const string SingboxLocalDNSTag = "local_local";
|
||||||
public const string SingboxHostsDNSTag = "hosts_dns";
|
public const string SingboxHostsDNSTag = "hosts_dns";
|
||||||
public const string SingboxFakeDNSTag = "fake_dns";
|
public const string SingboxFakeDNSTag = "fake_dns";
|
||||||
public const string SingboxEchDNSTag = "ech_dns";
|
|
||||||
|
|
||||||
public const int Hysteria2DefaultHopInt = 10;
|
public const int Hysteria2DefaultHopInt = 10;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,7 @@ public static class ConfigHandler
|
||||||
item.CertSha = profileItem.CertSha;
|
item.CertSha = profileItem.CertSha;
|
||||||
item.EchConfigList = profileItem.EchConfigList;
|
item.EchConfigList = profileItem.EchConfigList;
|
||||||
item.EchForceQuery = profileItem.EchForceQuery;
|
item.EchForceQuery = profileItem.EchForceQuery;
|
||||||
|
item.Finalmask = profileItem.Finalmask;
|
||||||
item.ProtoExtra = profileItem.ProtoExtra;
|
item.ProtoExtra = profileItem.ProtoExtra;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1155,6 +1156,7 @@ public static class ConfigHandler
|
||||||
&& AreEqual(o.Fingerprint, n.Fingerprint)
|
&& AreEqual(o.Fingerprint, n.Fingerprint)
|
||||||
&& AreEqual(o.PublicKey, n.PublicKey)
|
&& AreEqual(o.PublicKey, n.PublicKey)
|
||||||
&& AreEqual(o.ShortId, n.ShortId)
|
&& AreEqual(o.ShortId, n.ShortId)
|
||||||
|
&& AreEqual(o.Finalmask, n.Finalmask)
|
||||||
&& (!remarks || o.Remarks == n.Remarks);
|
&& (!remarks || o.Remarks == n.Remarks);
|
||||||
|
|
||||||
static bool AreEqual(string? a, string? b)
|
static bool AreEqual(string? a, string? b)
|
||||||
|
|
@ -1264,26 +1266,16 @@ public static class ConfigHandler
|
||||||
/// <param name="node">Server node that might need pre-SOCKS</param>
|
/// <param name="node">Server node that might need pre-SOCKS</param>
|
||||||
/// <param name="coreType">Core type being used</param>
|
/// <param name="coreType">Core type being used</param>
|
||||||
/// <returns>A SOCKS profile item or null if not needed</returns>
|
/// <returns>A SOCKS profile item or null if not needed</returns>
|
||||||
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
public static ProfileItem? GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
||||||
{
|
{
|
||||||
ProfileItem? itemSocks = null;
|
ProfileItem? itemSocks = null;
|
||||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
||||||
{
|
{
|
||||||
var tun2SocksAddress = node.Address;
|
|
||||||
if (node.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var lstAddresses = (await GroupProfileManager.GetAllChildDomainAddresses(node)).ToList();
|
|
||||||
if (lstAddresses.Count > 0)
|
|
||||||
{
|
|
||||||
tun2SocksAddress = Utils.List2String(lstAddresses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
itemSocks = new ProfileItem()
|
itemSocks = new ProfileItem()
|
||||||
{
|
{
|
||||||
CoreType = ECoreType.sing_box,
|
CoreType = ECoreType.sing_box,
|
||||||
ConfigType = EConfigType.SOCKS,
|
ConfigType = EConfigType.SOCKS,
|
||||||
Address = Global.Loopback,
|
Address = Global.Loopback,
|
||||||
SpiderX = tun2SocksAddress, // Tun2SocksAddress
|
|
||||||
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1298,7 +1290,6 @@ public static class ConfigHandler
|
||||||
Port = node.PreSocksPort.Value,
|
Port = node.PreSocksPort.Value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await Task.CompletedTask;
|
|
||||||
return itemSocks;
|
return itemSocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,27 +7,27 @@ public static class CoreConfigHandler
|
||||||
{
|
{
|
||||||
private static readonly string _tag = "CoreConfigHandler";
|
private static readonly string _tag = "CoreConfigHandler";
|
||||||
|
|
||||||
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
|
public static async Task<RetResult> GenerateClientConfig(CoreConfigContext context, string? fileName)
|
||||||
{
|
{
|
||||||
var config = AppManager.Instance.Config;
|
var config = AppManager.Instance.Config;
|
||||||
var result = new RetResult();
|
var result = new RetResult();
|
||||||
|
var node = context.Node;
|
||||||
|
|
||||||
if (node.ConfigType == EConfigType.Custom)
|
if (node.ConfigType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
result = node.CoreType switch
|
result = node.CoreType switch
|
||||||
{
|
{
|
||||||
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
|
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
|
||||||
ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
|
|
||||||
_ => await GenerateClientCustomConfig(node, fileName)
|
_ => await GenerateClientCustomConfig(node, fileName)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
else if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
|
result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
|
result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
|
||||||
}
|
}
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
|
|
@ -93,13 +93,21 @@ public static class CoreConfigHandler
|
||||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
|
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
|
||||||
{
|
{
|
||||||
var result = new RetResult();
|
var result = new RetResult();
|
||||||
|
var context = await BuildCoreConfigContext(config, new());
|
||||||
|
var ids = selecteds.Where(serverTestItem => !serverTestItem.IndexId.IsNullOrEmpty())
|
||||||
|
.Select(serverTestItem => serverTestItem.IndexId);
|
||||||
|
var nodes = await AppManager.Instance.GetProfileItemsByIndexIds(ids);
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
await FillNodeContext(context, node, false);
|
||||||
|
}
|
||||||
if (coreType == ECoreType.sing_box)
|
if (coreType == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
|
result = new CoreConfigSingboxService(context).GenerateClientSpeedtestConfig(selecteds);
|
||||||
}
|
}
|
||||||
else if (coreType == ECoreType.Xray)
|
else if (coreType == ECoreType.Xray)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
|
result = new CoreConfigV2rayService(context).GenerateClientSpeedtestConfig(selecteds);
|
||||||
}
|
}
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
|
|
@ -109,20 +117,21 @@ public static class CoreConfigHandler
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
|
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, CoreConfigContext context, ServerTestItem testItem, string fileName)
|
||||||
{
|
{
|
||||||
var result = new RetResult();
|
var result = new RetResult();
|
||||||
|
var node = context.Node;
|
||||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
|
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
|
||||||
testItem.Port = port;
|
testItem.Port = port;
|
||||||
|
|
||||||
if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
|
result = new CoreConfigSingboxService(context).GenerateClientSpeedtestConfig(port);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
|
result = new CoreConfigV2rayService(context).GenerateClientSpeedtestConfig(port);
|
||||||
}
|
}
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
|
|
@ -132,4 +141,127 @@ public static class CoreConfigHandler
|
||||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<CoreConfigContext> BuildCoreConfigContext(Config config, ProfileItem node)
|
||||||
|
{
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
|
var context = new CoreConfigContext()
|
||||||
|
{
|
||||||
|
Node = node,
|
||||||
|
AllProxiesMap = [],
|
||||||
|
AppConfig = config,
|
||||||
|
FullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(coreType),
|
||||||
|
IsTunEnabled = config.TunModeItem.EnableTun,
|
||||||
|
SimpleDnsItem = config.SimpleDNSItem,
|
||||||
|
ProtectDomainList = [],
|
||||||
|
ProtectSocksPort = 0,
|
||||||
|
RawDnsItem = await AppManager.Instance.GetDNSItem(coreType),
|
||||||
|
RoutingItem = await ConfigHandler.GetDefaultRouting(config),
|
||||||
|
};
|
||||||
|
context = context with
|
||||||
|
{
|
||||||
|
Node = await FillNodeContext(context, node)
|
||||||
|
};
|
||||||
|
if (!(context.RoutingItem?.RuleSet.IsNullOrEmpty() ?? true))
|
||||||
|
{
|
||||||
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(context.RoutingItem?.RuleSet);
|
||||||
|
foreach (var ruleItem in rules.Where(ruleItem => !Global.OutboundTags.Contains(ruleItem.OutboundTag)))
|
||||||
|
{
|
||||||
|
var ruleOutboundNode = await AppManager.Instance.GetProfileItemViaRemarks(ruleItem.OutboundTag);
|
||||||
|
if (ruleOutboundNode != null)
|
||||||
|
{
|
||||||
|
var ruleOutboundNodeAct = await FillNodeContext(context, ruleOutboundNode, false);
|
||||||
|
context.AllProxiesMap[$"remark:{ruleItem.OutboundTag}"] = ruleOutboundNodeAct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ProfileItem> FillNodeContext(CoreConfigContext context, ProfileItem node, bool includeSubChain = true)
|
||||||
|
{
|
||||||
|
if (node.IndexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
var newItems = new List<ProfileItem> { node };
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var (groupChildList, _) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
|
foreach (var childItem in groupChildList.Where(childItem => !context.AllProxiesMap.ContainsKey(childItem.IndexId)))
|
||||||
|
{
|
||||||
|
await FillNodeContext(context, childItem, false);
|
||||||
|
}
|
||||||
|
node.SetProtocolExtra(node.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
ChildItems = Utils.List2String(groupChildList.Select(n => n.IndexId).ToList()),
|
||||||
|
});
|
||||||
|
newItems.AddRange(groupChildList);
|
||||||
|
}
|
||||||
|
context.AllProxiesMap[node.IndexId] = node;
|
||||||
|
|
||||||
|
foreach (var item in newItems)
|
||||||
|
{
|
||||||
|
var address = item.Address;
|
||||||
|
if (Utils.IsDomain(address))
|
||||||
|
{
|
||||||
|
context.ProtectDomainList.Add(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var echQuerySni = item.Sni;
|
||||||
|
if (item.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& item.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = item.EchConfigList.IndexOf('+');
|
||||||
|
echQuerySni = idx > 0 ? item.EchConfigList[..idx] : item.Sni;
|
||||||
|
}
|
||||||
|
if (!Utils.IsDomain(echQuerySni))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
context.ProtectDomainList.Add(echQuerySni);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!includeSubChain || node.Subid.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||||
|
if (subItem == null)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
|
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||||
|
if (prevNode is null && nextNode is null)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevNodeAct = prevNode is null ? null : await FillNodeContext(context, prevNode, false);
|
||||||
|
var nextNodeAct = nextNode is null ? null : await FillNodeContext(context, nextNode, false);
|
||||||
|
|
||||||
|
// Build new proxy chain node
|
||||||
|
var chainNode = new ProfileItem()
|
||||||
|
{
|
||||||
|
IndexId = $"inner-{Utils.GetGuid(false)}",
|
||||||
|
ConfigType = EConfigType.ProxyChain,
|
||||||
|
CoreType = node.CoreType ?? ECoreType.Xray,
|
||||||
|
};
|
||||||
|
List<string?> childItems = [prevNodeAct?.IndexId, node.IndexId, nextNodeAct?.IndexId];
|
||||||
|
var chainExtraItem = chainNode.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
GroupType = chainNode.ConfigType.ToString(),
|
||||||
|
ChildItems = string.Join(",", childItems.Where(x => !x.IsNullOrEmpty())),
|
||||||
|
};
|
||||||
|
chainNode.SetProtocolExtra(chainExtraItem);
|
||||||
|
context.AllProxiesMap[chainNode.IndexId] = chainNode;
|
||||||
|
return chainNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,19 @@ public class BaseFmt
|
||||||
{
|
{
|
||||||
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
|
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
|
||||||
}
|
}
|
||||||
|
if (item.Finalmask.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var node = JsonUtils.ParseJson(item.Finalmask);
|
||||||
|
var finalmask = node != null
|
||||||
|
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = false,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
})
|
||||||
|
: item.Finalmask;
|
||||||
|
dicQuery.Add("fm", Utils.UrlEncode(finalmask));
|
||||||
|
}
|
||||||
|
|
||||||
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
||||||
|
|
||||||
|
|
@ -214,6 +227,24 @@ public class BaseFmt
|
||||||
item.EchConfigList = GetQueryDecoded(query, "ech");
|
item.EchConfigList = GetQueryDecoded(query, "ech");
|
||||||
item.CertSha = GetQueryDecoded(query, "pcs");
|
item.CertSha = GetQueryDecoded(query, "pcs");
|
||||||
|
|
||||||
|
var finalmaskDecoded = GetQueryDecoded(query, "fm");
|
||||||
|
if (finalmaskDecoded.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var node = JsonUtils.ParseJson(finalmaskDecoded);
|
||||||
|
item.Finalmask = node != null
|
||||||
|
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
})
|
||||||
|
: finalmaskDecoded;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Finalmask = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
||||||
{
|
{
|
||||||
item.AllowInsecure = Global.AllowInsecure.First();
|
item.AllowInsecure = Global.AllowInsecure.First();
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,18 @@ public sealed class AppManager
|
||||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<ProfileItem>> GetProfileItemsByIndexIds(IEnumerable<string> indexIds)
|
||||||
|
{
|
||||||
|
var ids = indexIds.Where(id => !id.IsNullOrEmpty()).Distinct().ToList();
|
||||||
|
if (ids.Count == 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return await SQLiteHelper.Instance.TableAsync<ProfileItem>()
|
||||||
|
.Where(it => ids.Contains(it.IndexId))
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
|
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
|
||||||
{
|
{
|
||||||
if (remarks.IsNullOrEmpty())
|
if (remarks.IsNullOrEmpty())
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ public class CertPemManager
|
||||||
using var client = new TcpClient();
|
using var client = new TcpClient();
|
||||||
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
||||||
|
|
||||||
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
await using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
||||||
|
|
||||||
var sslOptions = new SslClientAuthenticationOptions
|
var sslOptions = new SslClientAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
|
@ -262,7 +262,7 @@ public class CertPemManager
|
||||||
using var client = new TcpClient();
|
using var client = new TcpClient();
|
||||||
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
||||||
|
|
||||||
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
await using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
||||||
|
|
||||||
var sslOptions = new SslClientAuthenticationOptions
|
var sslOptions = new SslClientAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
|
@ -280,11 +280,7 @@ public class CertPemManager
|
||||||
var chain = new X509Chain();
|
var chain = new X509Chain();
|
||||||
chain.Build(certChain);
|
chain.Build(certChain);
|
||||||
|
|
||||||
foreach (var element in chain.ChainElements)
|
pemList.AddRange(chain.ChainElements.Select(element => ExportCertToPem(element.Certificate)));
|
||||||
{
|
|
||||||
var pem = ExportCertToPem(element.Certificate);
|
|
||||||
pemList.Add(pem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pemList, null);
|
return (pemList, null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,9 @@ public class CoreManager
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, node);
|
||||||
|
context = context with { IsTunEnabled = _config.TunModeItem.EnableTun };
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
await UpdateFunc(true, result.Msg);
|
await UpdateFunc(true, result.Msg);
|
||||||
|
|
@ -85,8 +87,8 @@ public class CoreManager
|
||||||
await WindowsUtils.RemoveTunDevice();
|
await WindowsUtils.RemoveTunDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CoreStart(node);
|
await CoreStart(context);
|
||||||
await CoreStartPreService(node);
|
await CoreStartPreService(context);
|
||||||
if (_processService != null)
|
if (_processService != null)
|
||||||
{
|
{
|
||||||
await UpdateFunc(true, $"{node.GetSummary()}");
|
await UpdateFunc(true, $"{node.GetSummary()}");
|
||||||
|
|
@ -122,7 +124,8 @@ public class CoreManager
|
||||||
|
|
||||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||||
var configPath = Utils.GetBinConfigPath(fileName);
|
var configPath = Utils.GetBinConfigPath(fileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, node);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, context, testItem, configPath);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -165,8 +168,9 @@ public class CoreManager
|
||||||
|
|
||||||
#region Private
|
#region Private
|
||||||
|
|
||||||
private async Task CoreStart(ProfileItem node)
|
private async Task CoreStart(CoreConfigContext context)
|
||||||
{
|
{
|
||||||
|
var node = context.Node;
|
||||||
var coreType = AppManager.Instance.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = AppManager.Instance.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||||
|
|
||||||
|
|
@ -179,17 +183,20 @@ public class CoreManager
|
||||||
_processService = proc;
|
_processService = proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CoreStartPreService(ProfileItem node)
|
private async Task CoreStartPreService(CoreConfigContext context)
|
||||||
{
|
{
|
||||||
|
var node = context.Node;
|
||||||
if (_processService != null && !_processService.HasExited)
|
if (_processService != null && !_processService.HasExited)
|
||||||
{
|
{
|
||||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
var itemSocks = ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
||||||
if (itemSocks != null)
|
if (itemSocks != null)
|
||||||
{
|
{
|
||||||
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
||||||
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
|
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
|
var itemSocksContext = await CoreConfigHandler.BuildCoreConfigContext(_config, itemSocks);
|
||||||
|
itemSocksContext.ProtectDomainList.AddRangeSafe(context.ProtectDomainList);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(itemSocksContext, fileName);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType);
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ public class GroupProfileManager
|
||||||
{
|
{
|
||||||
if (protocolExtra == null)
|
if (protocolExtra == null)
|
||||||
{
|
{
|
||||||
return new();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = new List<ProfileItem>();
|
var items = new List<ProfileItem>();
|
||||||
|
|
@ -93,27 +93,44 @@ public class GroupProfileManager
|
||||||
{
|
{
|
||||||
if (extra == null || extra.ChildItems.IsNullOrEmpty())
|
if (extra == null || extra.ChildItems.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return new();
|
return [];
|
||||||
}
|
}
|
||||||
var childProfiles = (await Task.WhenAll(
|
var childProfileIds = Utils.String2List(extra.ChildItems)
|
||||||
(Utils.String2List(extra.ChildItems) ?? new())
|
?.Where(p => !string.IsNullOrEmpty(p))
|
||||||
.Where(p => !p.IsNullOrEmpty())
|
.ToList() ?? [];
|
||||||
.Select(AppManager.Instance.GetProfileItem)
|
if (childProfileIds.Count == 0)
|
||||||
))
|
{
|
||||||
.Where(p =>
|
return [];
|
||||||
p != null &&
|
}
|
||||||
p.IsValid() &&
|
|
||||||
p.ConfigType != EConfigType.Custom
|
var childProfiles = await AppManager.Instance.GetProfileItemsByIndexIds(childProfileIds);
|
||||||
)
|
if (childProfiles == null || childProfiles.Count == 0)
|
||||||
.ToList();
|
{
|
||||||
return childProfiles ?? new();
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var profileMap = childProfiles
|
||||||
|
.Where(p => p != null && !p.IndexId.IsNullOrEmpty())
|
||||||
|
.GroupBy(p => p!.IndexId!)
|
||||||
|
.ToDictionary(g => g.Key, g => g.First());
|
||||||
|
|
||||||
|
var ordered = new List<ProfileItem>(childProfileIds.Count);
|
||||||
|
foreach (var id in childProfileIds)
|
||||||
|
{
|
||||||
|
if (id != null && profileMap.TryGetValue(id, out var item) && item != null)
|
||||||
|
{
|
||||||
|
ordered.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ordered;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra)
|
private static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra)
|
||||||
{
|
{
|
||||||
if (extra == null || extra.SubChildItems.IsNullOrEmpty())
|
if (extra == null || extra.SubChildItems.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return new();
|
return [];
|
||||||
}
|
}
|
||||||
var childProfiles = await AppManager.Instance.ProfileItems(extra.SubChildItems ?? string.Empty);
|
var childProfiles = await AppManager.Instance.ProfileItems(extra.SubChildItems ?? string.Empty);
|
||||||
|
|
||||||
|
|
@ -123,59 +140,30 @@ public class GroupProfileManager
|
||||||
!p.ConfigType.IsComplexType() &&
|
!p.ConfigType.IsComplexType() &&
|
||||||
(extra.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extra.Filter))
|
(extra.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extra.Filter))
|
||||||
)
|
)
|
||||||
.ToList() ?? new();
|
.ToList() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<HashSet<string>> GetAllChildDomainAddresses(ProfileItem profileItem)
|
public static async Task<List<ProfileItem>> GetAllChildProfileItems(ProfileItem profileItem)
|
||||||
{
|
{
|
||||||
var childAddresses = new HashSet<string>();
|
var allChildItems = new List<ProfileItem>();
|
||||||
var (childItems, _) = await GetChildProfileItems(profileItem);
|
var visited = new HashSet<string>();
|
||||||
foreach (var child in childItems)
|
|
||||||
{
|
await CollectChildItems(profileItem, allChildItems, visited);
|
||||||
if (!child.IsComplex())
|
|
||||||
{
|
return allChildItems;
|
||||||
childAddresses.Add(child.Address);
|
|
||||||
}
|
|
||||||
else if (child.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var subAddresses = await GetAllChildDomainAddresses(child);
|
|
||||||
foreach (var addr in subAddresses)
|
|
||||||
{
|
|
||||||
childAddresses.Add(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return childAddresses;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<HashSet<string>> GetAllChildEchQuerySni(ProfileItem profileItem)
|
private static async Task CollectChildItems(ProfileItem profileItem, List<ProfileItem> allChildItems, HashSet<string> visited)
|
||||||
{
|
{
|
||||||
var childAddresses = new HashSet<string>();
|
|
||||||
var (childItems, _) = await GetChildProfileItems(profileItem);
|
var (childItems, _) = await GetChildProfileItems(profileItem);
|
||||||
foreach (var childNode in childItems)
|
foreach (var child in childItems.Where(child => visited.Add(child.IndexId)))
|
||||||
{
|
{
|
||||||
if (!childNode.IsComplex() && !childNode.EchConfigList.IsNullOrEmpty())
|
allChildItems.Add(child);
|
||||||
|
|
||||||
|
if (child.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
if (childNode.StreamSecurity == Global.StreamSecurity
|
await CollectChildItems(child, allChildItems, visited);
|
||||||
&& childNode.EchConfigList?.Contains("://") == true)
|
|
||||||
{
|
|
||||||
var idx = childNode.EchConfigList.IndexOf('+');
|
|
||||||
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
childAddresses.Add(childNode.Sni);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (childNode.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var subAddresses = await GetAllChildDomainAddresses(childNode);
|
|
||||||
foreach (var addr in subAddresses)
|
|
||||||
{
|
|
||||||
childAddresses.Add(addr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return childAddresses;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
17
v2rayN/ServiceLib/Models/CoreConfigContext.cs
Normal file
17
v2rayN/ServiceLib/Models/CoreConfigContext.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
public record CoreConfigContext
|
||||||
|
{
|
||||||
|
public required ProfileItem Node { get; init; }
|
||||||
|
public RoutingItem? RoutingItem { get; init; }
|
||||||
|
public DNSItem? RawDnsItem { get; init; }
|
||||||
|
public SimpleDNSItem SimpleDnsItem { get; init; } = new();
|
||||||
|
public Dictionary<string, ProfileItem> AllProxiesMap { get; init; } = new();
|
||||||
|
public Config AppConfig { get; init; } = new();
|
||||||
|
public FullConfigTemplateItem? FullConfigTemplate { get; init; } = new();
|
||||||
|
|
||||||
|
// TUN Compatibility
|
||||||
|
public bool IsTunEnabled { get; init; } = false;
|
||||||
|
public HashSet<string> ProtectDomainList { get; init; } = new();
|
||||||
|
public int ProtectSocksPort { get; init; } = 0;
|
||||||
|
}
|
||||||
|
|
@ -178,6 +178,7 @@ public class ProfileItem
|
||||||
public string CertSha { get; set; }
|
public string CertSha { get; set; }
|
||||||
public string EchConfigList { get; set; }
|
public string EchConfigList { get; set; }
|
||||||
public string EchForceQuery { get; set; }
|
public string EchForceQuery { get; set; }
|
||||||
|
public string Finalmask { get; set; }
|
||||||
|
|
||||||
public string ProtoExtra { get; set; }
|
public string ProtoExtra { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,9 +259,8 @@ public class Server4Sbox : BaseServer4Sbox
|
||||||
// public List<string>? path { get; set; } // hosts
|
// public List<string>? path { get; set; } // hosts
|
||||||
public Dictionary<string, List<string>>? predefined { get; set; }
|
public Dictionary<string, List<string>>? predefined { get; set; }
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated in sing-box 1.12.0 , kept for backward compatibility
|
||||||
public string? address { get; set; }
|
public string? address { get; set; }
|
||||||
|
|
||||||
public string? address_resolver { get; set; }
|
public string? address_resolver { get; set; }
|
||||||
public string? address_strategy { get; set; }
|
public string? address_strategy { get; set; }
|
||||||
public string? strategy { get; set; }
|
public string? strategy { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,7 @@ public class StreamSettings4Ray
|
||||||
|
|
||||||
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
|
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
|
||||||
|
|
||||||
public FinalMask4Ray? finalmask { get; set; }
|
public Finalmask4Ray? finalmask { get; set; }
|
||||||
|
|
||||||
public Sockopt4Ray? sockopt { get; set; }
|
public Sockopt4Ray? sockopt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -478,7 +478,7 @@ public class HysteriaUdpHop4Ray
|
||||||
public string? interval { get; set; }
|
public string? interval { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FinalMask4Ray
|
public class Finalmask4Ray
|
||||||
{
|
{
|
||||||
public List<Mask4Ray>? tcp { get; set; }
|
public List<Mask4Ray>? tcp { get; set; }
|
||||||
public List<Mask4Ray>? udp { get; set; }
|
public List<Mask4Ray>? udp { get; set; }
|
||||||
|
|
@ -487,7 +487,7 @@ public class FinalMask4Ray
|
||||||
public class Mask4Ray
|
public class Mask4Ray
|
||||||
{
|
{
|
||||||
public string type { get; set; }
|
public string type { get; set; }
|
||||||
public MaskSettings4Ray? settings { get; set; }
|
public object? settings { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MaskSettings4Ray
|
public class MaskSettings4Ray
|
||||||
|
|
|
||||||
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -2925,6 +2925,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Finalmask 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbFinalmask {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbFinalmask", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Fingerprint 的本地化字符串。
|
/// 查找类似 Fingerprint 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1671,6 +1671,9 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerListPreview" xml:space="preserve">
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
<value>Configuration item preview</value>
|
<value>Configuration item preview</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
<data name="TbUot" xml:space="preserve">
|
<data name="TbUot" xml:space="preserve">
|
||||||
<value>UDP over TCP</value>
|
<value>UDP over TCP</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1668,6 +1668,9 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerListPreview" xml:space="preserve">
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
<value>Configuration item preview</value>
|
<value>Configuration item preview</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
<data name="TbUot" xml:space="preserve">
|
<data name="TbUot" xml:space="preserve">
|
||||||
<value>UDP over TCP</value>
|
<value>UDP over TCP</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1671,6 +1671,9 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerListPreview" xml:space="preserve">
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
<value>Configuration item preview</value>
|
<value>Configuration item preview</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
<data name="TbUot" xml:space="preserve">
|
<data name="TbUot" xml:space="preserve">
|
||||||
<value>UDP over TCP</value>
|
<value>UDP over TCP</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1671,6 +1671,9 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerListPreview" xml:space="preserve">
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
<value>Configuration item preview</value>
|
<value>Configuration item preview</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
<data name="TbUot" xml:space="preserve">
|
<data name="TbUot" xml:space="preserve">
|
||||||
<value>UDP over TCP</value>
|
<value>UDP over TCP</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1671,6 +1671,9 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerListPreview" xml:space="preserve">
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
<value>Configuration item preview</value>
|
<value>Configuration item preview</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
<data name="TbUot" xml:space="preserve">
|
<data name="TbUot" xml:space="preserve">
|
||||||
<value>UDP over TCP</value>
|
<value>UDP over TCP</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1668,6 +1668,9 @@
|
||||||
<data name="menuServerListPreview" xml:space="preserve">
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
<value>子配置项预览</value>
|
<value>子配置项预览</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
<data name="TbUot" xml:space="preserve">
|
<data name="TbUot" xml:space="preserve">
|
||||||
<value>UDP over TCP</value>
|
<value>UDP over TCP</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1668,6 +1668,9 @@
|
||||||
<data name="menuServerListPreview" xml:space="preserve">
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
<value>Configuration item preview</value>
|
<value>Configuration item preview</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
<data name="TbUot" xml:space="preserve">
|
<data name="TbUot" xml:space="preserve">
|
||||||
<value>UDP over TCP</value>
|
<value>UDP over TCP</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"log": {
|
"log": {
|
||||||
"access": "Vaccess.log",
|
"access": "Vaccess.log",
|
||||||
"error": "Verror.log",
|
"error": "Verror.log",
|
||||||
|
|
@ -6,34 +6,6 @@
|
||||||
},
|
},
|
||||||
"inbounds": [],
|
"inbounds": [],
|
||||||
"outbounds": [
|
"outbounds": [
|
||||||
{
|
|
||||||
"tag": "proxy",
|
|
||||||
"protocol": "vmess",
|
|
||||||
"settings": {
|
|
||||||
"vnext": [{
|
|
||||||
"address": "",
|
|
||||||
"port": 0,
|
|
||||||
"users": [{
|
|
||||||
"id": "",
|
|
||||||
"security": "auto"
|
|
||||||
}]
|
|
||||||
}],
|
|
||||||
"servers": [{
|
|
||||||
"address": "",
|
|
||||||
"method": "",
|
|
||||||
"ota": false,
|
|
||||||
"password": "",
|
|
||||||
"port": 0,
|
|
||||||
"level": 1
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
"streamSettings": {
|
|
||||||
"network": "tcp"
|
|
||||||
},
|
|
||||||
"mux": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"protocol": "freedom",
|
"protocol": "freedom",
|
||||||
"tag": "direct"
|
"tag": "direct"
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,12 @@
|
||||||
},
|
},
|
||||||
"inbounds": [],
|
"inbounds": [],
|
||||||
"outbounds": [
|
"outbounds": [
|
||||||
{
|
|
||||||
"type": "vless",
|
|
||||||
"tag": "proxy",
|
|
||||||
"server": "",
|
|
||||||
"server_port": 443
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "direct",
|
"type": "direct",
|
||||||
"tag": "direct"
|
"tag": "direct"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"route": {
|
"route": {
|
||||||
"rules": [
|
"rules": []
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,43 +1,34 @@
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService(Config config)
|
public partial class CoreConfigSingboxService(CoreConfigContext context)
|
||||||
{
|
{
|
||||||
private readonly Config _config = config;
|
|
||||||
private static readonly string _tag = "CoreConfigSingboxService";
|
private static readonly string _tag = "CoreConfigSingboxService";
|
||||||
|
private readonly Config _config = context.AppConfig;
|
||||||
|
private readonly ProfileItem _node = context.Node;
|
||||||
|
|
||||||
|
private SingboxConfig _coreConfig = new();
|
||||||
|
|
||||||
#region public gen function
|
#region public gen function
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
public RetResult GenerateClientConfigContent()
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (_node == null
|
||||||
|| !node.IsValid())
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
if (_node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
switch (node.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.PolicyGroup:
|
|
||||||
return await GenerateClientMultipleLoadConfig(node);
|
|
||||||
|
|
||||||
case EConfigType.ProxyChain:
|
|
||||||
return await GenerateClientChainConfig(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -45,44 +36,31 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
if (singboxConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
GenLog();
|
||||||
|
|
||||||
await GenInbounds(singboxConfig);
|
GenInbounds();
|
||||||
|
|
||||||
if (node.ConfigType == EConfigType.WireGuard)
|
GenOutbounds();
|
||||||
{
|
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
var endpoints = new Endpoints4Sbox();
|
|
||||||
await GenEndpoint(node, endpoints);
|
|
||||||
endpoints.tag = Global.ProxyTag;
|
|
||||||
singboxConfig.endpoints = new() { endpoints };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenMoreOutbounds(node, singboxConfig);
|
GenRouting();
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
GenDns();
|
||||||
|
|
||||||
await GenDns(node, singboxConfig);
|
GenExperimental();
|
||||||
|
|
||||||
await GenExperimental(singboxConfig);
|
ConvertGeo2Ruleset();
|
||||||
|
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
ret.Data = ApplyFullConfigTemplate();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -93,17 +71,11 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
public RetResult GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
|
|
@ -114,8 +86,8 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
if (singboxConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -133,10 +105,10 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
GenLog();
|
||||||
//GenDns(new(), singboxConfig);
|
GenMinimizedDns();
|
||||||
singboxConfig.inbounds.Clear();
|
_coreConfig.inbounds.Clear();
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
_coreConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
|
|
||||||
|
|
@ -150,7 +122,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
var item = context.AllProxiesMap.GetValueOrDefault(it.IndexId);
|
||||||
if (item is null || item.IsComplex() || !item.IsValid())
|
if (item is null || item.IsComplex() || !item.IsValid())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -190,26 +162,11 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
type = EInboundProtocol.mixed.ToString(),
|
type = EInboundProtocol.mixed.ToString(),
|
||||||
};
|
};
|
||||||
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
||||||
singboxConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
//outbound
|
|
||||||
var server = await GenServer(item);
|
|
||||||
if (server is null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
var tag = Global.ProxyTag + inbound.listen_port.ToString();
|
var tag = Global.ProxyTag + inbound.listen_port.ToString();
|
||||||
server.tag = tag;
|
var serverList = new CoreConfigSingboxService(context with { Node = item }).BuildAllProxyOutbounds(tag);
|
||||||
if (server is Endpoints4Sbox endpoint)
|
FillRangeProxy(serverList, _coreConfig, false);
|
||||||
{
|
|
||||||
singboxConfig.endpoints ??= new();
|
|
||||||
singboxConfig.endpoints.Add(endpoint);
|
|
||||||
}
|
|
||||||
else if (server is Outbound4Sbox outbound)
|
|
||||||
{
|
|
||||||
singboxConfig.outbounds.Add(outbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
//rule
|
//rule
|
||||||
Rule4Sbox rule = new()
|
Rule4Sbox rule = new()
|
||||||
|
|
@ -217,25 +174,11 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
inbound = new List<string> { inbound.tag },
|
inbound = new List<string> { inbound.tag },
|
||||||
outbound = tag
|
outbound = tag
|
||||||
};
|
};
|
||||||
singboxConfig.route.rules.Add(rule);
|
_coreConfig.route.rules.Add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
|
||||||
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
|
||||||
{
|
|
||||||
await GenDnsDomainsCompatible(singboxConfig, rawDNSItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenDnsDomains(singboxConfig, _config.SimpleDNSItem);
|
|
||||||
}
|
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
|
||||||
{
|
|
||||||
server = Global.SingboxLocalDNSTag,
|
|
||||||
};
|
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -246,20 +189,20 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
public RetResult GenerateClientSpeedtestConfig(int port)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (_node == null
|
||||||
|| !node.IsValid())
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
if (_node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,44 +215,20 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
if (singboxConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
GenLog();
|
||||||
if (node.ConfigType == EConfigType.WireGuard)
|
GenOutbounds();
|
||||||
{
|
GenMinimizedDns();
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
var endpoints = new Endpoints4Sbox();
|
|
||||||
await GenEndpoint(node, endpoints);
|
|
||||||
endpoints.tag = Global.ProxyTag;
|
|
||||||
singboxConfig.endpoints = new() { endpoints };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
|
||||||
}
|
|
||||||
await GenMoreOutbounds(node, singboxConfig);
|
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
|
||||||
if (item != null && item.Enabled == true)
|
|
||||||
{
|
|
||||||
await GenDnsDomainsCompatible(singboxConfig, item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenDnsDomains(singboxConfig, _config.SimpleDNSItem);
|
|
||||||
}
|
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
|
||||||
{
|
|
||||||
server = Global.SingboxLocalDNSTag,
|
|
||||||
};
|
|
||||||
|
|
||||||
singboxConfig.route.rules.Clear();
|
_coreConfig.route.rules.Clear();
|
||||||
singboxConfig.inbounds.Clear();
|
_coreConfig.inbounds.Clear();
|
||||||
singboxConfig.inbounds.Add(new()
|
_coreConfig.inbounds.Add(new()
|
||||||
{
|
{
|
||||||
tag = $"{EInboundProtocol.mixed}{port}",
|
tag = $"{EInboundProtocol.mixed}{port}",
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
|
|
@ -319,202 +238,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
|
||||||
if (singboxConfig == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
|
||||||
await GenInbounds(singboxConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
|
||||||
await GenExperimental(singboxConfig);
|
|
||||||
await GenDns(parentNode, singboxConfig);
|
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
|
||||||
if (singboxConfig == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
|
||||||
await GenInbounds(singboxConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
|
||||||
await GenExperimental(singboxConfig);
|
|
||||||
await GenDns(parentNode, singboxConfig);
|
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
if (node == null || fileName is null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (File.Exists(fileName))
|
|
||||||
{
|
|
||||||
File.Delete(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
var addressFileName = node.Address;
|
|
||||||
if (addressFileName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (!File.Exists(addressFileName))
|
|
||||||
{
|
|
||||||
addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName);
|
|
||||||
}
|
|
||||||
if (!File.Exists(addressFileName))
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedReadConfiguration + "1";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.Address == Global.CoreMultipleLoadConfigFileName)
|
|
||||||
{
|
|
||||||
var txtFile = File.ReadAllText(addressFileName);
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(txtFile);
|
|
||||||
if (singboxConfig == null)
|
|
||||||
{
|
|
||||||
File.Copy(addressFileName, fileName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenInbounds(singboxConfig);
|
|
||||||
await GenExperimental(singboxConfig);
|
|
||||||
|
|
||||||
var content = JsonUtils.Serialize(singboxConfig, true);
|
|
||||||
await File.WriteAllTextAsync(fileName, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File.Copy(addressFileName, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//check again
|
|
||||||
if (!File.Exists(fileName))
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedReadConfiguration + "2";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
|
||||||
ret.Success = true;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -2,29 +2,29 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<string> ApplyFullConfigTemplate(SingboxConfig singboxConfig)
|
private string ApplyFullConfigTemplate()
|
||||||
{
|
{
|
||||||
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
var fullConfigTemplate = context.FullConfigTemplate;
|
||||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled)
|
if (fullConfigTemplate is not { Enabled: true })
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(singboxConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullConfigTemplateItem = _config.TunModeItem.EnableTun ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config;
|
var fullConfigTemplateItem = context.IsTunEnabled ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config;
|
||||||
if (fullConfigTemplateItem.IsNullOrEmpty())
|
if (fullConfigTemplateItem.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(singboxConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem);
|
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem);
|
||||||
if (fullConfigTemplateNode == null)
|
if (fullConfigTemplateNode == null)
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(singboxConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process outbounds
|
// Process outbounds
|
||||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : [];
|
||||||
foreach (var outbound in singboxConfig.outbounds)
|
foreach (var outbound in _coreConfig.outbounds)
|
||||||
{
|
{
|
||||||
if (outbound.type.ToLower() is "direct" or "block")
|
if (outbound.type.ToLower() is "direct" or "block")
|
||||||
{
|
{
|
||||||
|
|
@ -42,10 +42,10 @@ public partial class CoreConfigSingboxService
|
||||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||||
|
|
||||||
// Process endpoints
|
// Process endpoints
|
||||||
if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0)
|
if (_coreConfig.endpoints != null && _coreConfig.endpoints.Count > 0)
|
||||||
{
|
{
|
||||||
var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray();
|
var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : [];
|
||||||
foreach (var endpoint in singboxConfig.endpoints)
|
foreach (var endpoint in _coreConfig.endpoints)
|
||||||
{
|
{
|
||||||
if (endpoint.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
if (endpoint.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -56,6 +56,6 @@ public partial class CoreConfigSingboxService
|
||||||
fullConfigTemplateNode["endpoints"] = customEndpointsNode;
|
fullConfigTemplateNode["endpoints"] = customEndpointsNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
return JsonUtils.Serialize(fullConfigTemplateNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,25 +2,25 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig)
|
private void GenDns()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var item = context.RawDnsItem;
|
||||||
if (item != null && item.Enabled == true)
|
if (item is { Enabled: true })
|
||||||
{
|
{
|
||||||
return await GenDnsCompatible(node, singboxConfig);
|
GenDnsCustom();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var simpleDNSItem = _config.SimpleDNSItem;
|
GenDnsServers();
|
||||||
await GenDnsServers(node, singboxConfig, simpleDNSItem);
|
GenDnsRules();
|
||||||
await GenDnsRules(node, singboxConfig, simpleDNSItem);
|
|
||||||
|
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.independent_cache = true;
|
_coreConfig.dns.independent_cache = true;
|
||||||
|
|
||||||
// final dns
|
// final dns
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
var useDirectDns = false;
|
var useDirectDns = false;
|
||||||
if (routing != null)
|
if (routing != null)
|
||||||
{
|
{
|
||||||
|
|
@ -32,35 +32,34 @@ public partial class CoreConfigSingboxService
|
||||||
lastRule.Network == "tcp,udp" ||
|
lastRule.Network == "tcp,udp" ||
|
||||||
lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
||||||
}
|
}
|
||||||
singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag;
|
_coreConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag;
|
||||||
if ((!useDirectDns) && simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false)
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
|
if ((!useDirectDns) && simpleDnsItem.FakeIP == true && simpleDnsItem.GlobalFakeIp == false)
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
_coreConfig.dns.rules.Add(new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxFakeDNSTag,
|
server = Global.SingboxFakeDNSTag,
|
||||||
query_type = new List<int> { 1, 28 }, // A and AAAA
|
query_type = new List<int> { 1, 28 }, // A and AAAA
|
||||||
rewrite_ttl = 1,
|
rewrite_ttl = 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsServers(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private void GenDnsServers()
|
||||||
{
|
{
|
||||||
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
|
var finalDns = GenBootstrapDns();
|
||||||
|
|
||||||
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
|
var directDns = ParseDnsAddress(simpleDnsItem.DirectDNS ?? Global.DomainDirectDNSAddress.First());
|
||||||
directDns.tag = Global.SingboxDirectDNSTag;
|
directDns.tag = Global.SingboxDirectDNSTag;
|
||||||
directDns.domain_resolver = Global.SingboxLocalDNSTag;
|
directDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
|
var remoteDns = ParseDnsAddress(simpleDnsItem.RemoteDNS ?? Global.DomainRemoteDNSAddress.First());
|
||||||
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
||||||
remoteDns.detour = Global.ProxyTag;
|
remoteDns.detour = Global.ProxyTag;
|
||||||
remoteDns.domain_resolver = Global.SingboxLocalDNSTag;
|
remoteDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
@ -71,12 +70,12 @@ public partial class CoreConfigSingboxService
|
||||||
type = "hosts",
|
type = "hosts",
|
||||||
predefined = new(),
|
predefined = new(),
|
||||||
};
|
};
|
||||||
if (simpleDNSItem.AddCommonHosts == true)
|
if (simpleDnsItem.AddCommonHosts == true)
|
||||||
{
|
{
|
||||||
hostsDns.predefined = Global.PredefinedHosts;
|
hostsDns.predefined = Global.PredefinedHosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.UseSystemHosts == true)
|
if (simpleDnsItem.UseSystemHosts == true)
|
||||||
{
|
{
|
||||||
var systemHosts = Utils.GetSystemHosts();
|
var systemHosts = Utils.GetSystemHosts();
|
||||||
if (systemHosts != null && systemHosts.Count > 0)
|
if (systemHosts != null && systemHosts.Count > 0)
|
||||||
|
|
@ -88,9 +87,9 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
|
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
||||||
{
|
{
|
||||||
hostsDns.predefined[kvp.Key] = kvp.Value.Where(s => Utils.IsIpAddress(s)).ToList();
|
hostsDns.predefined[kvp.Key] = kvp.Value.Where(Utils.IsIpAddress).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var host in hostsDns.predefined)
|
foreach (var host in hostsDns.predefined)
|
||||||
|
|
@ -109,14 +108,14 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.servers ??= [];
|
_coreConfig.dns.servers ??= [];
|
||||||
singboxConfig.dns.servers.Add(remoteDns);
|
_coreConfig.dns.servers.Add(remoteDns);
|
||||||
singboxConfig.dns.servers.Add(directDns);
|
_coreConfig.dns.servers.Add(directDns);
|
||||||
singboxConfig.dns.servers.Add(hostsDns);
|
_coreConfig.dns.servers.Add(hostsDns);
|
||||||
|
|
||||||
// fake ip
|
// fake ip
|
||||||
if (simpleDNSItem.FakeIP == true)
|
if (simpleDnsItem.FakeIP == true)
|
||||||
{
|
{
|
||||||
var fakeip = new Server4Sbox
|
var fakeip = new Server4Sbox
|
||||||
{
|
{
|
||||||
|
|
@ -125,68 +124,50 @@ public partial class CoreConfigSingboxService
|
||||||
inet4_range = "198.18.0.0/15",
|
inet4_range = "198.18.0.0/15",
|
||||||
inet6_range = "fc00::/18",
|
inet6_range = "fc00::/18",
|
||||||
};
|
};
|
||||||
singboxConfig.dns.servers.Add(fakeip);
|
_coreConfig.dns.servers.Add(fakeip);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ech
|
|
||||||
var (_, dnsServer) = ParseEchParam(node?.EchConfigList);
|
|
||||||
if (dnsServer is not null)
|
|
||||||
{
|
|
||||||
dnsServer.tag = Global.SingboxEchDNSTag;
|
|
||||||
if (dnsServer.server is not null
|
|
||||||
&& hostsDns.predefined.ContainsKey(dnsServer.server))
|
|
||||||
{
|
|
||||||
dnsServer.domain_resolver = Global.SingboxHostsDNSTag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dnsServer.domain_resolver = Global.SingboxLocalDNSTag;
|
|
||||||
}
|
|
||||||
singboxConfig.dns.servers.Add(dnsServer);
|
|
||||||
}
|
|
||||||
else if (node?.ConfigType.IsGroupType() == true)
|
|
||||||
{
|
|
||||||
var echDnsObject = JsonUtils.DeepCopy(directDns);
|
|
||||||
echDnsObject.tag = Global.SingboxEchDNSTag;
|
|
||||||
singboxConfig.dns.servers.Add(echDnsObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
|
private Server4Sbox GenBootstrapDns()
|
||||||
{
|
{
|
||||||
var finalDns = ParseDnsAddress(simpleDNSItem.BootstrapDNS);
|
var finalDns = ParseDnsAddress(context.SimpleDnsItem?.BootstrapDNS ?? Global.DomainPureIPDNSAddress.First());
|
||||||
finalDns.tag = Global.SingboxLocalDNSTag;
|
finalDns.tag = Global.SingboxLocalDNSTag;
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
_coreConfig.dns.servers ??= [];
|
||||||
singboxConfig.dns.servers.Add(finalDns);
|
_coreConfig.dns.servers.Add(finalDns);
|
||||||
return await Task.FromResult(finalDns);
|
return finalDns;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsRules(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private void GenDnsRules()
|
||||||
{
|
{
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
|
_coreConfig.dns.rules ??= [];
|
||||||
|
|
||||||
singboxConfig.dns.rules.AddRange(new[]
|
_coreConfig.dns.rules.AddRange(new[]
|
||||||
{
|
{
|
||||||
new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag },
|
new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag },
|
||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
|
{
|
||||||
|
server = Global.SingboxDirectDNSTag,
|
||||||
|
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom),
|
||||||
|
domain = context.ProtectDomainList.ToList(),
|
||||||
|
},
|
||||||
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxRemoteDNSTag,
|
server = Global.SingboxRemoteDNSTag,
|
||||||
strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Proxy),
|
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Proxy),
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
},
|
},
|
||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxDirectDNSTag,
|
server = Global.SingboxDirectDNSTag,
|
||||||
strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Freedom),
|
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom),
|
||||||
clash_mode = ERuleMode.Direct.ToString()
|
clash_mode = ERuleMode.Direct.ToString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
|
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
||||||
{
|
{
|
||||||
var predefined = kvp.Value.First();
|
var predefined = kvp.Value.First();
|
||||||
if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined))
|
if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined))
|
||||||
|
|
@ -197,7 +178,7 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
// xray syntactic sugar for predefined
|
// xray syntactic sugar for predefined
|
||||||
// etc. #0 -> NOERROR
|
// etc. #0 -> NOERROR
|
||||||
singboxConfig.dns.rules.Add(new()
|
_coreConfig.dns.rules.Add(new()
|
||||||
{
|
{
|
||||||
query_type = [1, 28],
|
query_type = [1, 28],
|
||||||
domain = [kvp.Key],
|
domain = [kvp.Key],
|
||||||
|
|
@ -225,38 +206,13 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
if (ParseV2Domain(kvp.Key, rule))
|
if (ParseV2Domain(kvp.Key, rule))
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(rule);
|
_coreConfig.dns.rules.Add(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (ech, _) = ParseEchParam(node?.EchConfigList);
|
if (simpleDnsItem.BlockBindingQuery == true)
|
||||||
if (ech is not null)
|
|
||||||
{
|
{
|
||||||
var echDomain = ech.query_server_name ?? node?.Sni;
|
_coreConfig.dns.rules.Add(new()
|
||||||
singboxConfig.dns.rules.Add(new()
|
|
||||||
{
|
|
||||||
query_type = [64, 65],
|
|
||||||
server = Global.SingboxEchDNSTag,
|
|
||||||
domain = echDomain is not null ? new List<string> { echDomain } : null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (node?.ConfigType.IsGroupType() == true)
|
|
||||||
{
|
|
||||||
var queryServerNames = (await GroupProfileManager.GetAllChildEchQuerySni(node)).ToList();
|
|
||||||
if (queryServerNames.Count > 0)
|
|
||||||
{
|
|
||||||
singboxConfig.dns.rules.Add(new()
|
|
||||||
{
|
|
||||||
query_type = [64, 65],
|
|
||||||
server = Global.SingboxEchDNSTag,
|
|
||||||
domain = queryServerNames,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simpleDNSItem.BlockBindingQuery == true)
|
|
||||||
{
|
|
||||||
singboxConfig.dns.rules.Add(new()
|
|
||||||
{
|
{
|
||||||
query_type = [64, 65],
|
query_type = [64, 65],
|
||||||
action = "predefined",
|
action = "predefined",
|
||||||
|
|
@ -264,7 +220,7 @@ public partial class CoreConfigSingboxService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == true)
|
if (simpleDnsItem.FakeIP == true && simpleDnsItem.GlobalFakeIp == true)
|
||||||
{
|
{
|
||||||
var fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
|
var fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
|
||||||
fakeipFilterRule.invert = true;
|
fakeipFilterRule.invert = true;
|
||||||
|
|
@ -284,13 +240,13 @@ public partial class CoreConfigSingboxService
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
singboxConfig.dns.rules.Add(rule4Fake);
|
_coreConfig.dns.rules.Add(rule4Fake);
|
||||||
}
|
}
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (routing == null)
|
if (routing == null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||||
|
|
@ -298,9 +254,9 @@ public partial class CoreConfigSingboxService
|
||||||
var expectedIPsRegions = new List<string>();
|
var expectedIPsRegions = new List<string>();
|
||||||
var regionNames = new HashSet<string>();
|
var regionNames = new HashSet<string>();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
if (!string.IsNullOrEmpty(simpleDnsItem?.DirectExpectedIPs))
|
||||||
{
|
{
|
||||||
var ipItems = simpleDNSItem.DirectExpectedIPs
|
var ipItems = simpleDnsItem.DirectExpectedIPs
|
||||||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(s => s.Trim())
|
.Select(s => s.Trim())
|
||||||
.Where(s => !string.IsNullOrEmpty(s))
|
.Where(s => !string.IsNullOrEmpty(s))
|
||||||
|
|
@ -348,7 +304,7 @@ public partial class CoreConfigSingboxService
|
||||||
if (item.OutboundTag == Global.DirectTag)
|
if (item.OutboundTag == Global.DirectTag)
|
||||||
{
|
{
|
||||||
rule.server = Global.SingboxDirectDNSTag;
|
rule.server = Global.SingboxDirectDNSTag;
|
||||||
rule.strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Freedom);
|
rule.strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom);
|
||||||
|
|
||||||
if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0)
|
if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -373,31 +329,46 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false)
|
if (simpleDnsItem.FakeIP == true && simpleDnsItem.GlobalFakeIp == false)
|
||||||
{
|
{
|
||||||
var rule4Fake = JsonUtils.DeepCopy(rule);
|
var rule4Fake = JsonUtils.DeepCopy(rule);
|
||||||
rule4Fake.server = Global.SingboxFakeDNSTag;
|
rule4Fake.server = Global.SingboxFakeDNSTag;
|
||||||
rule4Fake.query_type = new List<int> { 1, 28 }; // A and AAAA
|
rule4Fake.query_type = new List<int> { 1, 28 }; // A and AAAA
|
||||||
rule4Fake.rewrite_ttl = 1;
|
rule4Fake.rewrite_ttl = 1;
|
||||||
singboxConfig.dns.rules.Add(rule4Fake);
|
_coreConfig.dns.rules.Add(rule4Fake);
|
||||||
}
|
}
|
||||||
rule.server = Global.SingboxRemoteDNSTag;
|
rule.server = Global.SingboxRemoteDNSTag;
|
||||||
rule.strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Proxy);
|
rule.strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.dns.rules.Add(rule);
|
_coreConfig.dns.rules.Add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsCompatible(ProfileItem? node, SingboxConfig singboxConfig)
|
private void GenMinimizedDns()
|
||||||
|
{
|
||||||
|
GenDnsServers();
|
||||||
|
foreach (var server in _coreConfig.dns!.servers.Where(s => !string.IsNullOrEmpty(s.detour)).ToList())
|
||||||
|
{
|
||||||
|
_coreConfig.dns.servers.Remove(server);
|
||||||
|
}
|
||||||
|
_coreConfig.dns ??= new();
|
||||||
|
_coreConfig.dns.rules ??= [];
|
||||||
|
_coreConfig.dns.rules.Clear();
|
||||||
|
_coreConfig.dns.final = Global.SingboxDirectDNSTag;
|
||||||
|
_coreConfig.route.default_domain_resolver = new()
|
||||||
|
{
|
||||||
|
server = Global.SingboxDirectDNSTag,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenDnsCustom()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var item = context.RawDnsItem;
|
||||||
var strDNS = string.Empty;
|
var strDNS = string.Empty;
|
||||||
if (_config.TunModeItem.EnableTun)
|
if (context.IsTunEnabled)
|
||||||
{
|
{
|
||||||
strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS;
|
strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS;
|
||||||
}
|
}
|
||||||
|
|
@ -409,61 +380,33 @@ public partial class CoreConfigSingboxService
|
||||||
var dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(strDNS);
|
var dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(strDNS);
|
||||||
if (dns4Sbox is null)
|
if (dns4Sbox is null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
singboxConfig.dns = dns4Sbox;
|
_coreConfig.dns = dns4Sbox;
|
||||||
|
if (dns4Sbox.servers?.Count > 0 &&
|
||||||
if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty())
|
dns4Sbox.servers.First().address.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
await GenDnsDomainsCompatible(singboxConfig, item);
|
GenDnsProtectCustom();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
|
GenDnsProtectCustomLegacy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
private void GenDnsProtectCustom()
|
||||||
{
|
{
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
var dnsItem = context.RawDnsItem;
|
||||||
|
var dns4Sbox = _coreConfig.dns ?? new();
|
||||||
dns4Sbox.servers ??= [];
|
dns4Sbox.servers ??= [];
|
||||||
dns4Sbox.rules ??= [];
|
dns4Sbox.rules ??= [];
|
||||||
|
|
||||||
var tag = Global.SingboxLocalDNSTag;
|
var tag = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
|
|
||||||
|
|
||||||
var localDnsServer = ParseDnsAddress(finalDnsAddress);
|
|
||||||
localDnsServer.tag = tag;
|
|
||||||
|
|
||||||
dns4Sbox.servers.Add(localDnsServer);
|
|
||||||
|
|
||||||
singboxConfig.dns = dns4Sbox;
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
|
||||||
{
|
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
|
||||||
dns4Sbox.servers ??= [];
|
|
||||||
dns4Sbox.rules ??= [];
|
|
||||||
|
|
||||||
var tag = Global.SingboxLocalDNSTag;
|
|
||||||
dns4Sbox.servers.Add(new()
|
|
||||||
{
|
|
||||||
tag = tag,
|
|
||||||
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
|
||||||
detour = Global.DirectTag,
|
|
||||||
strategy = string.IsNullOrEmpty(dnsItem?.DomainStrategy4Freedom) ? null : dnsItem?.DomainStrategy4Freedom,
|
|
||||||
});
|
|
||||||
dns4Sbox.rules.Insert(0, new()
|
dns4Sbox.rules.Insert(0, new()
|
||||||
{
|
{
|
||||||
server = tag,
|
server = tag,
|
||||||
|
|
@ -475,56 +418,41 @@ public partial class CoreConfigSingboxService
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
});
|
});
|
||||||
|
|
||||||
var lstDomain = singboxConfig.outbounds
|
var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
|
||||||
.Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server))
|
|
||||||
.Select(t => t.server)
|
|
||||||
.Distinct()
|
|
||||||
.ToList();
|
|
||||||
if (lstDomain != null && lstDomain.Count > 0)
|
|
||||||
{
|
|
||||||
dns4Sbox.rules.Insert(0, new()
|
|
||||||
{
|
|
||||||
server = tag,
|
|
||||||
domain = lstDomain
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
singboxConfig.dns = dns4Sbox;
|
var localDnsServer = ParseDnsAddress(finalDnsAddress);
|
||||||
return await Task.FromResult(0);
|
localDnsServer.tag = tag;
|
||||||
|
|
||||||
|
dns4Sbox.servers.Add(localDnsServer);
|
||||||
|
dns4Sbox.rules.Insert(0, BuildProtectDomainRule());
|
||||||
|
|
||||||
|
_coreConfig.dns = dns4Sbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig)
|
private void GenDnsProtectCustomLegacy()
|
||||||
{
|
{
|
||||||
if (node == null)
|
GenDnsProtectCustom();
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<string> domain = new();
|
_coreConfig.dns?.servers?.RemoveAll(s => s.tag == Global.SingboxLocalDNSTag);
|
||||||
if (Utils.IsDomain(node.Address)) // normal outbound
|
var dnsItem = context.RawDnsItem;
|
||||||
|
var localDnsServer = new Server4Sbox()
|
||||||
{
|
{
|
||||||
domain.Add(node.Address);
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress)
|
||||||
}
|
? Global.DomainPureIPDNSAddress.FirstOrDefault()
|
||||||
if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty()) // Tun2SocksAddress
|
: dnsItem?.DomainDNSAddress,
|
||||||
{
|
tag = Global.SingboxLocalDNSTag,
|
||||||
domain.AddRange(Utils.String2List(node.SpiderX)
|
detour = Global.DirectTag,
|
||||||
.Where(Utils.IsDomain)
|
};
|
||||||
.Distinct()
|
_coreConfig.dns?.servers?.Add(localDnsServer);
|
||||||
.ToList());
|
}
|
||||||
}
|
|
||||||
if (domain.Count == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
private Rule4Sbox BuildProtectDomainRule()
|
||||||
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
|
{
|
||||||
|
return new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxLocalDNSTag,
|
server = Global.SingboxLocalDNSTag,
|
||||||
domain = domain,
|
domain = context.ProtectDomainList.ToList(),
|
||||||
});
|
};
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Server4Sbox? ParseDnsAddress(string address)
|
private static Server4Sbox? ParseDnsAddress(string address)
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenInbounds(SingboxConfig singboxConfig)
|
private void GenInbounds()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var listen = "0.0.0.0";
|
var listen = "0.0.0.0";
|
||||||
singboxConfig.inbounds = [];
|
_coreConfig.inbounds = [];
|
||||||
|
|
||||||
if (!_config.TunModeItem.EnableTun
|
if (!_config.TunModeItem.EnableTun
|
||||||
|| (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && AppManager.Instance.RunningCoreType == ECoreType.sing_box))
|
|| (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && AppManager.Instance.RunningCoreType == ECoreType.sing_box))
|
||||||
|
|
@ -18,23 +18,23 @@ public partial class CoreConfigSingboxService
|
||||||
tag = EInboundProtocol.socks.ToString(),
|
tag = EInboundProtocol.socks.ToString(),
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
};
|
};
|
||||||
singboxConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
inbound.listen_port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
inbound.listen_port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||||
|
|
||||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||||
{
|
{
|
||||||
var inbound2 = GetInbound(inbound, EInboundProtocol.socks2, true);
|
var inbound2 = BuildInbound(inbound, EInboundProtocol.socks2, true);
|
||||||
singboxConfig.inbounds.Add(inbound2);
|
_coreConfig.inbounds.Add(inbound2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Inbound.First().AllowLANConn)
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
{
|
{
|
||||||
if (_config.Inbound.First().NewPort4LAN)
|
if (_config.Inbound.First().NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = GetInbound(inbound, EInboundProtocol.socks3, true);
|
var inbound3 = BuildInbound(inbound, EInboundProtocol.socks3, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
singboxConfig.inbounds.Add(inbound3);
|
_coreConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
||||||
|
|
@ -71,17 +71,16 @@ public partial class CoreConfigSingboxService
|
||||||
tunInbound.address = ["172.18.0.1/30"];
|
tunInbound.address = ["172.18.0.1/30"];
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.inbounds.Add(tunInbound);
|
_coreConfig.inbounds.Add(tunInbound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Inbound4Sbox GetInbound(Inbound4Sbox inItem, EInboundProtocol protocol, bool bSocks)
|
private Inbound4Sbox BuildInbound(Inbound4Sbox inItem, EInboundProtocol protocol, bool bSocks)
|
||||||
{
|
{
|
||||||
var inbound = JsonUtils.DeepCopy(inItem);
|
var inbound = JsonUtils.DeepCopy(inItem);
|
||||||
inbound.tag = protocol.ToString();
|
inbound.tag = protocol.ToString();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenLog(SingboxConfig singboxConfig)
|
private void GenLog()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -11,11 +11,11 @@ public partial class CoreConfigSingboxService
|
||||||
case "debug":
|
case "debug":
|
||||||
case "info":
|
case "info":
|
||||||
case "error":
|
case "error":
|
||||||
singboxConfig.log.level = _config.CoreBasicItem.Loglevel;
|
_coreConfig.log.level = _config.CoreBasicItem.Loglevel;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "warning":
|
case "warning":
|
||||||
singboxConfig.log.level = "warn";
|
_coreConfig.log.level = "warn";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -23,18 +23,17 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
if (_config.CoreBasicItem.Loglevel == Global.None)
|
if (_config.CoreBasicItem.Loglevel == Global.None)
|
||||||
{
|
{
|
||||||
singboxConfig.log.disabled = true;
|
_coreConfig.log.disabled = true;
|
||||||
}
|
}
|
||||||
if (_config.CoreBasicItem.LogEnabled)
|
if (_config.CoreBasicItem.LogEnabled)
|
||||||
{
|
{
|
||||||
var dtNow = DateTime.Now;
|
var dtNow = DateTime.Now;
|
||||||
singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt");
|
_coreConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,23 +2,23 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenRouting(SingboxConfig singboxConfig)
|
private void GenRouting()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
singboxConfig.route.final = Global.ProxyTag;
|
_coreConfig.route.final = Global.ProxyTag;
|
||||||
var simpleDnsItem = _config.SimpleDNSItem;
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
|
|
||||||
var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
|
var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
|
||||||
var directDnsStrategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom);
|
var directDnsStrategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom);
|
||||||
|
|
||||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var rawDNSItem = context.RawDnsItem;
|
||||||
if (rawDNSItem is { Enabled: true })
|
if (rawDNSItem is { Enabled: true })
|
||||||
{
|
{
|
||||||
defaultDomainResolverTag = Global.SingboxLocalDNSTag;
|
defaultDomainResolverTag = Global.SingboxLocalDNSTag;
|
||||||
directDnsStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? null : rawDNSItem.DomainStrategy4Freedom;
|
directDnsStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? null : rawDNSItem.DomainStrategy4Freedom;
|
||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
_coreConfig.route.default_domain_resolver = new()
|
||||||
{
|
{
|
||||||
server = defaultDomainResolverTag,
|
server = defaultDomainResolverTag,
|
||||||
strategy = directDnsStrategy
|
strategy = directDnsStrategy
|
||||||
|
|
@ -26,23 +26,23 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (_config.TunModeItem.EnableTun)
|
if (_config.TunModeItem.EnableTun)
|
||||||
{
|
{
|
||||||
singboxConfig.route.auto_detect_interface = true;
|
_coreConfig.route.auto_detect_interface = true;
|
||||||
|
|
||||||
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName));
|
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName));
|
||||||
if (tunRules != null)
|
if (tunRules != null)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.AddRange(tunRules);
|
_coreConfig.route.rules.AddRange(tunRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
GenRoutingDirectExe(out var lstDnsExe, out var lstDirectExe);
|
var (lstDnsExe, lstDirectExe) = BuildRoutingDirectExe();
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
port = new() { 53 },
|
port = [53],
|
||||||
action = "hijack-dns",
|
action = "hijack-dns",
|
||||||
process_name = lstDnsExe
|
process_name = lstDnsExe
|
||||||
});
|
});
|
||||||
|
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
outbound = Global.DirectTag,
|
outbound = Global.DirectTag,
|
||||||
process_name = lstDirectExe
|
process_name = lstDirectExe
|
||||||
|
|
@ -51,66 +51,62 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (_config.Inbound.First().SniffingEnabled)
|
if (_config.Inbound.First().SniffingEnabled)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
action = "sniff"
|
action = "sniff"
|
||||||
});
|
});
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
protocol = new() { "dns" },
|
protocol = ["dns"],
|
||||||
action = "hijack-dns"
|
action = "hijack-dns"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
port = new() { 53 },
|
port = [53],
|
||||||
network = new() { "udp" },
|
network = ["udp"],
|
||||||
action = "hijack-dns"
|
action = "hijack-dns"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostsDomains = new List<string>();
|
var hostsDomains = new List<string>();
|
||||||
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
if (rawDNSItem is not { Enabled: true })
|
||||||
if (dnsItem == null || !dnsItem.Enabled)
|
|
||||||
{
|
{
|
||||||
var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts);
|
var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts);
|
||||||
hostsDomains.AddRange(userHostsMap.Select(kvp => kvp.Key));
|
hostsDomains.AddRange(userHostsMap.Select(kvp => kvp.Key));
|
||||||
if (simpleDnsItem.UseSystemHosts == true)
|
if (simpleDnsItem.UseSystemHosts == true)
|
||||||
{
|
{
|
||||||
var systemHostsMap = Utils.GetSystemHosts();
|
var systemHostsMap = Utils.GetSystemHosts();
|
||||||
foreach (var kvp in systemHostsMap)
|
hostsDomains.AddRange(systemHostsMap.Select(kvp => kvp.Key));
|
||||||
{
|
|
||||||
hostsDomains.Add(kvp.Key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hostsDomains.Count > 0)
|
if (hostsDomains.Count > 0)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
action = "resolve",
|
action = "resolve",
|
||||||
domain = hostsDomains,
|
domain = hostsDomains,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
outbound = Global.DirectTag,
|
outbound = Global.DirectTag,
|
||||||
clash_mode = ERuleMode.Direct.ToString()
|
clash_mode = ERuleMode.Direct.ToString()
|
||||||
});
|
});
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
outbound = Global.ProxyTag,
|
outbound = Global.ProxyTag,
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
});
|
});
|
||||||
|
|
||||||
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.NullIfEmpty();
|
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.NullIfEmpty();
|
||||||
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
|
if (routing.DomainStrategy4Singbox.IsNotEmpty())
|
||||||
{
|
{
|
||||||
domainStrategy = defaultRouting.DomainStrategy4Singbox;
|
domainStrategy = routing.DomainStrategy4Singbox;
|
||||||
}
|
}
|
||||||
var resolveRule = new Rule4Sbox
|
var resolveRule = new Rule4Sbox
|
||||||
{
|
{
|
||||||
|
|
@ -119,10 +115,9 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand)
|
if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(resolveRule);
|
_coreConfig.route.rules.Add(resolveRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
|
||||||
var ipRules = new List<RulesItem>();
|
var ipRules = new List<RulesItem>();
|
||||||
if (routing != null)
|
if (routing != null)
|
||||||
{
|
{
|
||||||
|
|
@ -139,7 +134,7 @@ public partial class CoreConfigSingboxService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenRoutingUserRule(item1, singboxConfig);
|
GenRoutingUserRule(item1);
|
||||||
|
|
||||||
if (item1.Ip?.Count > 0)
|
if (item1.Ip?.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -149,10 +144,10 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch)
|
if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(resolveRule);
|
_coreConfig.route.rules.Add(resolveRule);
|
||||||
foreach (var item2 in ipRules)
|
foreach (var item2 in ipRules)
|
||||||
{
|
{
|
||||||
await GenRoutingUserRule(item2, singboxConfig);
|
GenRoutingUserRule(item2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,10 +155,9 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenRoutingDirectExe(out List<string> lstDnsExe, out List<string> lstDirectExe)
|
private static (List<string> lstDnsExe, List<string> lstDirectExe) BuildRoutingDirectExe()
|
||||||
{
|
{
|
||||||
var dnsExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var dnsExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
@ -187,20 +181,22 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lstDnsExe = new List<string>(dnsExeSet);
|
var lstDnsExe = new List<string>(dnsExeSet);
|
||||||
lstDirectExe = new List<string>(directExeSet);
|
var lstDirectExe = new List<string>(directExeSet);
|
||||||
|
|
||||||
|
return (lstDnsExe, lstDirectExe);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenRoutingUserRule(RulesItem item, SingboxConfig singboxConfig)
|
private void GenRoutingUserRule(RulesItem? item)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig);
|
item.OutboundTag = GenRoutingUserRuleOutbound(item.OutboundTag ?? Global.ProxyTag);
|
||||||
var rules = singboxConfig.route.rules;
|
var rules = _coreConfig.route.rules;
|
||||||
|
|
||||||
var rule = new Rule4Sbox();
|
var rule = new Rule4Sbox();
|
||||||
if (item.OutboundTag == "block")
|
if (item.OutboundTag == "block")
|
||||||
|
|
@ -326,10 +322,9 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseV2Domain(string domain, Rule4Sbox rule)
|
private static bool ParseV2Domain(string domain, Rule4Sbox rule)
|
||||||
{
|
{
|
||||||
if (domain.StartsWith('#') || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:"))
|
if (domain.StartsWith('#') || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:"))
|
||||||
{
|
{
|
||||||
|
|
@ -368,7 +363,7 @@ public partial class CoreConfigSingboxService
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseV2Address(string address, Rule4Sbox rule)
|
private static bool ParseV2Address(string address, Rule4Sbox rule)
|
||||||
{
|
{
|
||||||
if (address.StartsWith("ext:") || address.StartsWith("ext-ip:"))
|
if (address.StartsWith("ext:") || address.StartsWith("ext-ip:"))
|
||||||
{
|
{
|
||||||
|
|
@ -401,14 +396,14 @@ public partial class CoreConfigSingboxService
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, SingboxConfig singboxConfig)
|
private string GenRoutingUserRuleOutbound(string outboundTag)
|
||||||
{
|
{
|
||||||
if (Global.OutboundTags.Contains(outboundTag))
|
if (Global.OutboundTags.Contains(outboundTag))
|
||||||
{
|
{
|
||||||
return outboundTag;
|
return outboundTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
var node = context.AllProxiesMap.GetValueOrDefault($"remark:{outboundTag}");
|
||||||
|
|
||||||
if (node == null
|
if (node == null
|
||||||
|| (!Global.SingboxSupportConfigType.Contains(node.ConfigType)
|
|| (!Global.SingboxSupportConfigType.Contains(node.ConfigType)
|
||||||
|
|
@ -418,39 +413,15 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
||||||
if (singboxConfig.outbounds.Any(o => o.tag == tag)
|
if (_coreConfig.outbounds.Any(o => o.tag.StartsWith(tag))
|
||||||
|| (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag)))
|
|| (_coreConfig.endpoints != null && _coreConfig.endpoints.Any(e => e.tag.StartsWith(tag))))
|
||||||
{
|
{
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
var proxyOutbounds = new CoreConfigSingboxService(context with { Node = node, }).BuildAllProxyOutbounds(tag);
|
||||||
{
|
FillRangeProxy(proxyOutbounds, _coreConfig, false);
|
||||||
var ret = await GenGroupOutbound(node, singboxConfig, tag);
|
|
||||||
if (ret == 0)
|
|
||||||
{
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
return Global.ProxyTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
var server = await GenServer(node);
|
return tag;
|
||||||
if (server is null)
|
|
||||||
{
|
|
||||||
return Global.ProxyTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.tag = tag;
|
|
||||||
if (server is Endpoints4Sbox endpoint)
|
|
||||||
{
|
|
||||||
singboxConfig.endpoints ??= new();
|
|
||||||
singboxConfig.endpoints.Add(endpoint);
|
|
||||||
}
|
|
||||||
else if (server is Outbound4Sbox outbound)
|
|
||||||
{
|
|
||||||
singboxConfig.outbounds.Add(outbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.tag;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> ConvertGeo2Ruleset(SingboxConfig singboxConfig)
|
private void ConvertGeo2Ruleset()
|
||||||
{
|
{
|
||||||
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
||||||
{
|
{
|
||||||
|
|
@ -16,14 +16,14 @@ public partial class CoreConfigSingboxService
|
||||||
var ruleSets = new List<string>();
|
var ruleSets = new List<string>();
|
||||||
|
|
||||||
//convert route geosite & geoip to ruleset
|
//convert route geosite & geoip to ruleset
|
||||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||||
rule.geosite = null;
|
rule.geosite = null;
|
||||||
AddRuleSets(ruleSets, rule.rule_set);
|
AddRuleSets(ruleSets, rule.rule_set);
|
||||||
}
|
}
|
||||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||||
|
|
@ -32,24 +32,24 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
//convert dns geosite & geoip to ruleset
|
//convert dns geosite & geoip to ruleset
|
||||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||||
rule.geosite = null;
|
rule.geosite = null;
|
||||||
}
|
}
|
||||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||||
rule.geoip = null;
|
rule.geoip = null;
|
||||||
}
|
}
|
||||||
foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
|
foreach (var dnsRule in _coreConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
AddRuleSets(ruleSets, dnsRule.rule_set);
|
AddRuleSets(ruleSets, dnsRule.rule_set);
|
||||||
}
|
}
|
||||||
//rules in rules
|
//rules in rules
|
||||||
foreach (var item in singboxConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? [])
|
foreach (var item in _coreConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? [])
|
||||||
{
|
{
|
||||||
foreach (var item2 in item ?? [])
|
foreach (var item2 in item ?? [])
|
||||||
{
|
{
|
||||||
|
|
@ -60,7 +60,7 @@ public partial class CoreConfigSingboxService
|
||||||
//load custom ruleset file
|
//load custom ruleset file
|
||||||
List<Ruleset4Sbox> customRulesets = [];
|
List<Ruleset4Sbox> customRulesets = [];
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (routing.CustomRulesetPath4Singbox.IsNotEmpty())
|
if (routing.CustomRulesetPath4Singbox.IsNotEmpty())
|
||||||
{
|
{
|
||||||
var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox);
|
var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox);
|
||||||
|
|
@ -78,7 +78,7 @@ public partial class CoreConfigSingboxService
|
||||||
var localSrss = Utils.GetBinPath("srss");
|
var localSrss = Utils.GetBinPath("srss");
|
||||||
|
|
||||||
//Add ruleset srs
|
//Add ruleset srs
|
||||||
singboxConfig.route.rule_set = [];
|
_coreConfig.route.rule_set = [];
|
||||||
foreach (var item in new HashSet<string>(ruleSets))
|
foreach (var item in new HashSet<string>(ruleSets))
|
||||||
{
|
{
|
||||||
if (item.IsNullOrEmpty())
|
if (item.IsNullOrEmpty())
|
||||||
|
|
@ -113,9 +113,7 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
singboxConfig.route.rule_set.Add(customRuleset);
|
_coreConfig.route.rule_set.Add(customRuleset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenExperimental(SingboxConfig singboxConfig)
|
private void GenExperimental()
|
||||||
{
|
{
|
||||||
//if (_config.guiItem.enableStatistics)
|
//if (_config.guiItem.enableStatistics)
|
||||||
{
|
{
|
||||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
_coreConfig.experimental ??= new Experimental4Sbox();
|
||||||
singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
|
_coreConfig.experimental.clash_api = new Clash_Api4Sbox()
|
||||||
{
|
{
|
||||||
external_controller = $"{Global.Loopback}:{AppManager.Instance.StatePort2}",
|
external_controller = $"{Global.Loopback}:{AppManager.Instance.StatePort2}",
|
||||||
};
|
};
|
||||||
|
|
@ -15,15 +15,13 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (_config.CoreBasicItem.EnableCacheFile4Sbox)
|
if (_config.CoreBasicItem.EnableCacheFile4Sbox)
|
||||||
{
|
{
|
||||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
_coreConfig.experimental ??= new Experimental4Sbox();
|
||||||
singboxConfig.experimental.cache_file = new CacheFile4Sbox()
|
_coreConfig.experimental.cache_file = new CacheFile4Sbox()
|
||||||
{
|
{
|
||||||
enabled = true,
|
enabled = true,
|
||||||
path = Utils.GetBinPath("cache.db"),
|
path = Utils.GetBinPath("cache.db"),
|
||||||
store_fakeip = _config.SimpleDNSItem.FakeIP == true
|
store_fakeip = context.SimpleDnsItem.FakeIP == true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,35 @@
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService(Config config)
|
public partial class CoreConfigV2rayService(CoreConfigContext context)
|
||||||
{
|
{
|
||||||
private readonly Config _config = config;
|
|
||||||
private static readonly string _tag = "CoreConfigV2rayService";
|
private static readonly string _tag = "CoreConfigV2rayService";
|
||||||
|
private readonly Config _config = context.AppConfig;
|
||||||
|
private readonly ProfileItem _node = context.Node;
|
||||||
|
|
||||||
|
private V2rayConfig _coreConfig = new();
|
||||||
|
|
||||||
#region public gen function
|
#region public gen function
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
public RetResult GenerateClientConfigContent()
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (_node == null
|
||||||
|| !node.IsValid())
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.GetNetwork() is nameof(ETransport.quic))
|
if (_node.GetNetwork() is nameof(ETransport.quic))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
switch (node.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.PolicyGroup:
|
|
||||||
return await GenerateClientMultipleLoadConfig(node);
|
|
||||||
|
|
||||||
case EConfigType.ProxyChain:
|
|
||||||
return await GenerateClientChainConfig(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -46,30 +37,28 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
if (v2rayConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
GenLog();
|
||||||
|
|
||||||
await GenInbounds(v2rayConfig);
|
GenInbounds();
|
||||||
|
|
||||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
GenOutbounds();
|
||||||
|
|
||||||
await GenMoreOutbounds(node, v2rayConfig);
|
GenRouting();
|
||||||
|
|
||||||
await GenRouting(v2rayConfig);
|
GenDns();
|
||||||
|
|
||||||
await GenDns(node, v2rayConfig);
|
GenStatistic();
|
||||||
|
|
||||||
await GenStatistic(v2rayConfig);
|
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
ret.Data = ApplyFullConfigTemplate();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -80,18 +69,11 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
|
public RetResult GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
|
|
@ -102,172 +84,8 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
if (v2rayConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
v2rayConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
|
||||||
await GenInbounds(v2rayConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenRouting(v2rayConfig);
|
|
||||||
await GenDns(null, v2rayConfig);
|
|
||||||
await GenStatistic(v2rayConfig);
|
|
||||||
|
|
||||||
var defaultBalancerTag = $"{Global.ProxyTag}{Global.BalancerTagSuffix}";
|
|
||||||
|
|
||||||
//add rule
|
|
||||||
var rules = v2rayConfig.routing.rules;
|
|
||||||
if (rules?.Count > 0 && ((v2rayConfig.routing.balancers?.Count ?? 0) > 0))
|
|
||||||
{
|
|
||||||
var balancerTagSet = v2rayConfig.routing.balancers
|
|
||||||
.Select(b => b.tag)
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
foreach (var rule in rules)
|
|
||||||
{
|
|
||||||
if (rule.outboundTag == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (balancerTagSet.Contains(rule.outboundTag))
|
|
||||||
{
|
|
||||||
rule.balancerTag = rule.outboundTag;
|
|
||||||
rule.outboundTag = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var outboundWithSuffix = rule.outboundTag + Global.BalancerTagSuffix;
|
|
||||||
if (balancerTagSet.Contains(outboundWithSuffix))
|
|
||||||
{
|
|
||||||
rule.balancerTag = outboundWithSuffix;
|
|
||||||
rule.outboundTag = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
|
||||||
{
|
|
||||||
v2rayConfig.routing.rules.Add(new()
|
|
||||||
{
|
|
||||||
ip = ["0.0.0.0/0", "::/0"],
|
|
||||||
balancerTag = defaultBalancerTag,
|
|
||||||
type = "field"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v2rayConfig.routing.rules.Add(new()
|
|
||||||
{
|
|
||||||
network = "tcp,udp",
|
|
||||||
balancerTag = defaultBalancerTag,
|
|
||||||
type = "field"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
|
||||||
if (v2rayConfig == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
v2rayConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
|
||||||
await GenInbounds(v2rayConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenRouting(v2rayConfig);
|
|
||||||
await GenDns(null, v2rayConfig);
|
|
||||||
await GenStatistic(v2rayConfig);
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
|
||||||
if (v2rayConfig == null)
|
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -285,10 +103,10 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
GenLog();
|
||||||
v2rayConfig.inbounds.Clear();
|
_coreConfig.inbounds.Clear();
|
||||||
v2rayConfig.outbounds.Clear();
|
_coreConfig.outbounds.Clear();
|
||||||
v2rayConfig.routing.rules.Clear();
|
_coreConfig.routing.rules.Clear();
|
||||||
|
|
||||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
|
|
||||||
|
|
@ -302,7 +120,7 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
var item = context.AllProxiesMap.GetValueOrDefault(it.IndexId);
|
||||||
if (item is null || item.IsComplex() || !item.IsValid())
|
if (item is null || item.IsComplex() || !item.IsValid())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -342,27 +160,40 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
protocol = EInboundProtocol.mixed.ToString(),
|
protocol = EInboundProtocol.mixed.ToString(),
|
||||||
};
|
};
|
||||||
inbound.tag = inbound.protocol + inbound.port.ToString();
|
inbound.tag = inbound.protocol + inbound.port.ToString();
|
||||||
v2rayConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
|
var tag = Global.ProxyTag + inbound.port.ToString();
|
||||||
|
var isBalancer = false;
|
||||||
//outbound
|
//outbound
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var proxyOutbounds =
|
||||||
await GenOutbound(item, outbound);
|
new CoreConfigV2rayService(context with { Node = item }).BuildAllProxyOutbounds(tag);
|
||||||
outbound.tag = Global.ProxyTag + inbound.port.ToString();
|
_coreConfig.outbounds.AddRange(proxyOutbounds);
|
||||||
v2rayConfig.outbounds.Add(outbound);
|
if (proxyOutbounds.Count(n => n.tag.StartsWith(tag)) > 1)
|
||||||
|
{
|
||||||
|
isBalancer = true;
|
||||||
|
var multipleLoad = _node.GetProtocolExtra().MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||||
|
GenObservatory(multipleLoad, tag);
|
||||||
|
GenBalancer(multipleLoad, tag);
|
||||||
|
}
|
||||||
|
|
||||||
//rule
|
//rule
|
||||||
RulesItem4Ray rule = new()
|
RulesItem4Ray rule = new()
|
||||||
{
|
{
|
||||||
inboundTag = new List<string> { inbound.tag },
|
inboundTag = new List<string> { inbound.tag },
|
||||||
outboundTag = outbound.tag,
|
outboundTag = tag,
|
||||||
type = "field"
|
type = "field"
|
||||||
};
|
};
|
||||||
v2rayConfig.routing.rules.Add(rule);
|
if (isBalancer)
|
||||||
|
{
|
||||||
|
rule.balancerTag = tag;
|
||||||
|
rule.outboundTag = null;
|
||||||
|
}
|
||||||
|
_coreConfig.routing.rules.Add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
|
//ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -373,21 +204,21 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
public RetResult GenerateClientSpeedtestConfig(int port)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (_node == null
|
||||||
|| !node.IsValid())
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.GetNetwork() is nameof(ETransport.quic))
|
if (_node.GetNetwork() is nameof(ETransport.quic))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -398,20 +229,19 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
if (v2rayConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
GenLog();
|
||||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
GenOutbounds();
|
||||||
await GenMoreOutbounds(node, v2rayConfig);
|
|
||||||
|
|
||||||
v2rayConfig.routing.rules.Clear();
|
_coreConfig.routing.rules.Clear();
|
||||||
v2rayConfig.inbounds.Clear();
|
_coreConfig.inbounds.Clear();
|
||||||
v2rayConfig.inbounds.Add(new()
|
_coreConfig.inbounds.Add(new()
|
||||||
{
|
{
|
||||||
tag = $"{EInboundProtocol.socks}{port}",
|
tag = $"{EInboundProtocol.socks}{port}",
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
|
|
@ -421,7 +251,7 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenObservatory(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
|
private void GenObservatory(EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
// Collect all existing subject selectors from both observatories
|
// Collect all existing subject selectors from both observatories
|
||||||
var subjectSelectors = new List<string>();
|
var subjectSelectors = new List<string>();
|
||||||
subjectSelectors.AddRange(v2rayConfig.burstObservatory?.subjectSelector ?? []);
|
subjectSelectors.AddRange(_coreConfig.burstObservatory?.subjectSelector ?? []);
|
||||||
subjectSelectors.AddRange(v2rayConfig.observatory?.subjectSelector ?? []);
|
subjectSelectors.AddRange(_coreConfig.observatory?.subjectSelector ?? []);
|
||||||
|
|
||||||
// Case 1: exact match already exists -> nothing to do
|
// Case 1: exact match already exists -> nothing to do
|
||||||
if (subjectSelectors.Any(baseTagName.StartsWith))
|
if (subjectSelectors.Any(baseTagName.StartsWith))
|
||||||
{
|
{
|
||||||
return await Task.FromResult(0);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: prefix match exists -> reuse it and move to the first position
|
// Case 2: prefix match exists -> reuse it and move to the first position
|
||||||
|
|
@ -21,28 +21,28 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
baseTagName = matched;
|
baseTagName = matched;
|
||||||
|
|
||||||
if (v2rayConfig.burstObservatory?.subjectSelector?.Contains(baseTagName) == true)
|
if (_coreConfig.burstObservatory?.subjectSelector?.Contains(baseTagName) == true)
|
||||||
{
|
{
|
||||||
v2rayConfig.burstObservatory.subjectSelector.Remove(baseTagName);
|
_coreConfig.burstObservatory.subjectSelector.Remove(baseTagName);
|
||||||
v2rayConfig.burstObservatory.subjectSelector.Insert(0, baseTagName);
|
_coreConfig.burstObservatory.subjectSelector.Insert(0, baseTagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2rayConfig.observatory?.subjectSelector?.Contains(baseTagName) == true)
|
if (_coreConfig.observatory?.subjectSelector?.Contains(baseTagName) == true)
|
||||||
{
|
{
|
||||||
v2rayConfig.observatory.subjectSelector.Remove(baseTagName);
|
_coreConfig.observatory.subjectSelector.Remove(baseTagName);
|
||||||
v2rayConfig.observatory.subjectSelector.Insert(0, baseTagName);
|
_coreConfig.observatory.subjectSelector.Insert(0, baseTagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 3: need to create or insert based on multipleLoad type
|
// Case 3: need to create or insert based on multipleLoad type
|
||||||
if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
|
if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
|
||||||
{
|
{
|
||||||
if (v2rayConfig.burstObservatory is null)
|
if (_coreConfig.burstObservatory is null)
|
||||||
{
|
{
|
||||||
// Create new burst observatory with default ping config
|
// Create new burst observatory with default ping config
|
||||||
v2rayConfig.burstObservatory = new BurstObservatory4Ray
|
_coreConfig.burstObservatory = new BurstObservatory4Ray
|
||||||
{
|
{
|
||||||
subjectSelector = [baseTagName],
|
subjectSelector = [baseTagName],
|
||||||
pingConfig = new()
|
pingConfig = new()
|
||||||
|
|
@ -56,16 +56,16 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v2rayConfig.burstObservatory.subjectSelector ??= new();
|
_coreConfig.burstObservatory.subjectSelector ??= new();
|
||||||
v2rayConfig.burstObservatory.subjectSelector.Add(baseTagName);
|
_coreConfig.burstObservatory.subjectSelector.Add(baseTagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (multipleLoad is EMultipleLoad.LeastPing)
|
else if (multipleLoad is EMultipleLoad.LeastPing)
|
||||||
{
|
{
|
||||||
if (v2rayConfig.observatory is null)
|
if (_coreConfig.observatory is null)
|
||||||
{
|
{
|
||||||
// Create new observatory with default probe config
|
// Create new observatory with default probe config
|
||||||
v2rayConfig.observatory = new Observatory4Ray
|
_coreConfig.observatory = new Observatory4Ray
|
||||||
{
|
{
|
||||||
subjectSelector = [baseTagName],
|
subjectSelector = [baseTagName],
|
||||||
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||||
|
|
@ -75,15 +75,13 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v2rayConfig.observatory.subjectSelector ??= new();
|
_coreConfig.observatory.subjectSelector ??= new();
|
||||||
v2rayConfig.observatory.subjectSelector.Add(baseTagName);
|
_coreConfig.observatory.subjectSelector.Add(baseTagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
|
private void GenBalancer(EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
var strategyType = multipleLoad switch
|
var strategyType = multipleLoad switch
|
||||||
{
|
{
|
||||||
|
|
@ -107,8 +105,7 @@ public partial class CoreConfigV2rayService
|
||||||
},
|
},
|
||||||
tag = balancerTag,
|
tag = balancerTag,
|
||||||
};
|
};
|
||||||
v2rayConfig.routing.balancers ??= new();
|
_coreConfig.routing.balancers ??= new();
|
||||||
v2rayConfig.routing.balancers.Add(balancer);
|
_coreConfig.routing.balancers.Add(balancer);
|
||||||
return await Task.FromResult(balancerTag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,39 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig)
|
private string ApplyFullConfigTemplate()
|
||||||
{
|
{
|
||||||
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
var fullConfigTemplate = context.FullConfigTemplate;
|
||||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(v2rayConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config);
|
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config);
|
||||||
if (fullConfigTemplateNode == null)
|
if (fullConfigTemplateNode == null)
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(v2rayConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle balancer and rules modifications (for multiple load scenarios)
|
// Handle balancer and rules modifications (for multiple load scenarios)
|
||||||
if (v2rayConfig.routing?.balancers?.Count > 0)
|
if (_coreConfig.routing?.balancers?.Count > 0)
|
||||||
{
|
{
|
||||||
var balancer = v2rayConfig.routing.balancers.First();
|
var balancer =
|
||||||
|
_coreConfig.routing.balancers.FirstOrDefault(b => b.tag == Global.ProxyTag + Global.BalancerTagSuffix, null);
|
||||||
|
|
||||||
// Modify existing rules in custom config
|
// Modify existing rules in custom config
|
||||||
var rulesNode = fullConfigTemplateNode["routing"]?["rules"];
|
if (balancer != null)
|
||||||
if (rulesNode != null)
|
|
||||||
{
|
{
|
||||||
foreach (var rule in rulesNode.AsArray())
|
var rulesNode = fullConfigTemplateNode["routing"]?["rules"];
|
||||||
|
if (rulesNode != null)
|
||||||
{
|
{
|
||||||
if (rule["outboundTag"]?.GetValue<string>() == Global.ProxyTag)
|
foreach (var rule in rulesNode.AsArray())
|
||||||
{
|
{
|
||||||
rule.AsObject().Remove("outboundTag");
|
if (rule["outboundTag"]?.GetValue<string>() == Global.ProxyTag)
|
||||||
rule["balancerTag"] = balancer.tag;
|
{
|
||||||
|
rule.AsObject().Remove("outboundTag");
|
||||||
|
rule["balancerTag"] = balancer.tag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +48,7 @@ public partial class CoreConfigV2rayService
|
||||||
// Handle balancers - append instead of override
|
// Handle balancers - append instead of override
|
||||||
if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode)
|
if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode)
|
||||||
{
|
{
|
||||||
if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers)
|
if (JsonNode.Parse(JsonUtils.Serialize(_coreConfig.routing.balancers)) is JsonArray newBalancers)
|
||||||
{
|
{
|
||||||
foreach (var balancerNode in newBalancers)
|
foreach (var balancerNode in newBalancers)
|
||||||
{
|
{
|
||||||
|
|
@ -54,33 +58,33 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers));
|
fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(_coreConfig.routing.balancers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2rayConfig.observatory != null)
|
if (_coreConfig.observatory != null)
|
||||||
{
|
{
|
||||||
if (fullConfigTemplateNode["observatory"] == null)
|
if (fullConfigTemplateNode["observatory"] == null)
|
||||||
{
|
{
|
||||||
fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.observatory));
|
fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(_coreConfig.observatory));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var subjectSelector = v2rayConfig.observatory.subjectSelector;
|
var subjectSelector = _coreConfig.observatory.subjectSelector;
|
||||||
subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
||||||
fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2rayConfig.burstObservatory != null)
|
if (_coreConfig.burstObservatory != null)
|
||||||
{
|
{
|
||||||
if (fullConfigTemplateNode["burstObservatory"] == null)
|
if (fullConfigTemplateNode["burstObservatory"] == null)
|
||||||
{
|
{
|
||||||
fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.burstObservatory));
|
fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(_coreConfig.burstObservatory));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var subjectSelector = v2rayConfig.burstObservatory.subjectSelector;
|
var subjectSelector = _coreConfig.burstObservatory.subjectSelector;
|
||||||
subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
||||||
fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +92,7 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
var customOutboundsNode = new JsonArray();
|
var customOutboundsNode = new JsonArray();
|
||||||
|
|
||||||
foreach (var outbound in v2rayConfig.outbounds)
|
foreach (var outbound in _coreConfig.outbounds)
|
||||||
{
|
{
|
||||||
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
||||||
{
|
{
|
||||||
|
|
@ -97,8 +101,8 @@ public partial class CoreConfigV2rayService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
else if (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()
|
||||||
&& ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true) == true))
|
&& (outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true))
|
||||||
{
|
{
|
||||||
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
|
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
|
||||||
?? outbound.settings?.vnext?.FirstOrDefault()?.address
|
?? outbound.settings?.vnext?.FirstOrDefault()?.address
|
||||||
|
|
@ -123,6 +127,6 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||||
|
|
||||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
return JsonUtils.Serialize(fullConfigTemplateNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,46 +2,45 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig)
|
private void GenDns()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
var item = context.RawDnsItem;
|
||||||
if (item is { Enabled: true })
|
if (item is { Enabled: true })
|
||||||
{
|
{
|
||||||
var result = await GenDnsCompatible(node, v2rayConfig);
|
GenDnsCustom();
|
||||||
|
|
||||||
if (v2rayConfig.routing.domainStrategy != Global.IPIfNonMatch)
|
if (_coreConfig.routing.domainStrategy != Global.IPIfNonMatch)
|
||||||
{
|
{
|
||||||
return result;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS routing
|
// DNS routing
|
||||||
var dnsObj = JsonUtils.SerializeToNode(v2rayConfig.dns);
|
var dnsObj = JsonUtils.SerializeToNode(_coreConfig.dns);
|
||||||
if (dnsObj == null)
|
if (dnsObj == null)
|
||||||
{
|
{
|
||||||
return result;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsObj["tag"] = Global.DnsTag;
|
dnsObj["tag"] = Global.DnsTag;
|
||||||
v2rayConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(dnsObj));
|
_coreConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(dnsObj));
|
||||||
v2rayConfig.routing.rules.Add(new RulesItem4Ray
|
_coreConfig.routing.rules.Add(new RulesItem4Ray
|
||||||
{
|
{
|
||||||
type = "field",
|
type = "field",
|
||||||
inboundTag = new List<string> { Global.DnsTag },
|
inboundTag = new List<string> { Global.DnsTag },
|
||||||
outboundTag = Global.ProxyTag,
|
outboundTag = Global.ProxyTag,
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
var simpleDnsItem = _config.SimpleDNSItem;
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
var dnsItem = v2rayConfig.dns is Dns4Ray dns4Ray ? dns4Ray : new Dns4Ray();
|
var dnsItem = _coreConfig.dns is Dns4Ray dns4Ray ? dns4Ray : new Dns4Ray();
|
||||||
|
|
||||||
var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs;
|
var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs;
|
||||||
//Outbound Freedom domainStrategy
|
//Outbound Freedom domainStrategy
|
||||||
if (strategy4Freedom.IsNotEmpty() && strategy4Freedom != Global.AsIs)
|
if (strategy4Freedom.IsNotEmpty() && strategy4Freedom != Global.AsIs)
|
||||||
{
|
{
|
||||||
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
var outbound = _coreConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||||
if (outbound != null)
|
if (outbound != null)
|
||||||
{
|
{
|
||||||
outbound.settings = new()
|
outbound.settings = new()
|
||||||
|
|
@ -59,23 +58,23 @@ public partial class CoreConfigV2rayService
|
||||||
var xraySupportConfigTypeNames = Global.XraySupportConfigType
|
var xraySupportConfigTypeNames = Global.XraySupportConfigType
|
||||||
.Select(x => x == EConfigType.Hysteria2 ? "hysteria" : Global.ProtocolTypes[x])
|
.Select(x => x == EConfigType.Hysteria2 ? "hysteria" : Global.ProtocolTypes[x])
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
v2rayConfig.outbounds
|
_coreConfig.outbounds
|
||||||
.Where(t => xraySupportConfigTypeNames.Contains(t.protocol))
|
.Where(t => xraySupportConfigTypeNames.Contains(t.protocol))
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(outbound => outbound.targetStrategy = strategy4Proxy);
|
.ForEach(outbound => outbound.targetStrategy = strategy4Proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenDnsServers(node, dnsItem, simpleDnsItem);
|
FillDnsServers(dnsItem);
|
||||||
await GenDnsHosts(dnsItem, simpleDnsItem);
|
FillDnsHosts(dnsItem);
|
||||||
|
|
||||||
dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null;
|
dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null;
|
||||||
dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null;
|
dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null;
|
||||||
|
|
||||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
if (_coreConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
||||||
{
|
{
|
||||||
// DNS routing
|
// DNS routing
|
||||||
dnsItem.tag = Global.DnsTag;
|
dnsItem.tag = Global.DnsTag;
|
||||||
v2rayConfig.routing.rules.Add(new RulesItem4Ray
|
_coreConfig.routing.rules.Add(new RulesItem4Ray
|
||||||
{
|
{
|
||||||
type = "field",
|
type = "field",
|
||||||
inboundTag = new List<string> { Global.DnsTag },
|
inboundTag = new List<string> { Global.DnsTag },
|
||||||
|
|
@ -83,17 +82,17 @@ public partial class CoreConfigV2rayService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
v2rayConfig.dns = dnsItem;
|
_coreConfig.dns = dnsItem;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsServers(ProfileItem? node, Dns4Ray dnsItem, SimpleDNSItem simpleDNSItem)
|
private void FillDnsServers(Dns4Ray dnsItem)
|
||||||
{
|
{
|
||||||
|
var simpleDNSItem = context.SimpleDnsItem;
|
||||||
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
|
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
|
||||||
{
|
{
|
||||||
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
|
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
|
||||||
|
|
@ -197,9 +196,9 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
List<RulesItem>? rules = null;
|
List<RulesItem>? rules = null;
|
||||||
rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
rules = JsonUtils.Deserialize<List<RulesItem>>(routing?.RuleSet) ?? [];
|
||||||
foreach (var item in rules)
|
foreach (var item in rules)
|
||||||
{
|
{
|
||||||
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
|
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
|
||||||
|
|
@ -246,27 +245,9 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils.IsDomain(node?.Address))
|
if (context.ProtectDomainList.Count > 0)
|
||||||
{
|
{
|
||||||
directDomainList.Add(node.Address);
|
directDomainList.AddRange(context.ProtectDomainList);
|
||||||
}
|
|
||||||
|
|
||||||
if (node?.Subid is not null)
|
|
||||||
{
|
|
||||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
|
||||||
if (subItem is not null)
|
|
||||||
{
|
|
||||||
foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile })
|
|
||||||
{
|
|
||||||
var profileNode = await AppManager.Instance.GetProfileItemViaRemarks(profile);
|
|
||||||
if (profileNode is not null
|
|
||||||
&& Global.XraySupportConfigType.Contains(profileNode.ConfigType)
|
|
||||||
&& Utils.IsDomain(profileNode.Address))
|
|
||||||
{
|
|
||||||
directDomainList.Add(profileNode.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsItem.servers ??= [];
|
dnsItem.servers ??= [];
|
||||||
|
|
@ -300,15 +281,14 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
||||||
dnsItem.servers.AddRange(defaultDnsServers);
|
dnsItem.servers.AddRange(defaultDnsServers);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsHosts(Dns4Ray dnsItem, SimpleDNSItem simpleDNSItem)
|
private void FillDnsHosts(Dns4Ray dnsItem)
|
||||||
{
|
{
|
||||||
|
var simpleDNSItem = context.SimpleDnsItem;
|
||||||
if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty())
|
if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return await Task.FromResult(0);
|
return;
|
||||||
}
|
}
|
||||||
dnsItem.hosts ??= new Dictionary<string, object>();
|
dnsItem.hosts ??= new Dictionary<string, object>();
|
||||||
if (simpleDNSItem.AddCommonHosts == true)
|
if (simpleDNSItem.AddCommonHosts == true)
|
||||||
|
|
@ -337,14 +317,13 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
dnsItem.hosts[kvp.Key] = kvp.Value;
|
dnsItem.hosts[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsCompatible(ProfileItem? node, V2rayConfig v2rayConfig)
|
private void GenDnsCustom()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
var item = context.RawDnsItem;
|
||||||
var normalDNS = item?.NormalDNS;
|
var normalDNS = item?.NormalDNS;
|
||||||
var domainStrategy4Freedom = item?.DomainStrategy4Freedom;
|
var domainStrategy4Freedom = item?.DomainStrategy4Freedom;
|
||||||
if (normalDNS.IsNullOrEmpty())
|
if (normalDNS.IsNullOrEmpty())
|
||||||
|
|
@ -355,7 +334,7 @@ public partial class CoreConfigV2rayService
|
||||||
//Outbound Freedom domainStrategy
|
//Outbound Freedom domainStrategy
|
||||||
if (domainStrategy4Freedom.IsNotEmpty())
|
if (domainStrategy4Freedom.IsNotEmpty())
|
||||||
{
|
{
|
||||||
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
var outbound = _coreConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||||
if (outbound != null)
|
if (outbound != null)
|
||||||
{
|
{
|
||||||
outbound.settings = new();
|
outbound.settings = new();
|
||||||
|
|
@ -410,63 +389,37 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenDnsDomainsCompatible(node, obj, item);
|
FillDnsDomainsCustom(obj);
|
||||||
|
|
||||||
v2rayConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(obj));
|
_coreConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(obj));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dnsItem)
|
private void FillDnsDomainsCustom(JsonNode dns)
|
||||||
{
|
{
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
var servers = dns["servers"];
|
var servers = dns["servers"];
|
||||||
if (servers != null)
|
if (servers == null)
|
||||||
{
|
{
|
||||||
var domainList = new List<string>();
|
return;
|
||||||
if (Utils.IsDomain(node.Address))
|
|
||||||
{
|
|
||||||
domainList.Add(node.Address);
|
|
||||||
}
|
|
||||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
|
||||||
if (subItem is not null)
|
|
||||||
{
|
|
||||||
// Previous proxy
|
|
||||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
|
||||||
if (prevNode is not null
|
|
||||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)
|
|
||||||
&& Utils.IsDomain(prevNode.Address))
|
|
||||||
{
|
|
||||||
domainList.Add(prevNode.Address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next proxy
|
|
||||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
|
||||||
if (nextNode is not null
|
|
||||||
&& Global.SingboxSupportConfigType.Contains(nextNode.ConfigType)
|
|
||||||
&& Utils.IsDomain(nextNode.Address))
|
|
||||||
{
|
|
||||||
domainList.Add(nextNode.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (domainList.Count > 0)
|
|
||||||
{
|
|
||||||
var dnsServer = new DnsServer4Ray()
|
|
||||||
{
|
|
||||||
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
|
||||||
skipFallback = true,
|
|
||||||
domains = domainList
|
|
||||||
};
|
|
||||||
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
|
var domainList = context.ProtectDomainList;
|
||||||
|
if (domainList.Count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dnsItem = context.RawDnsItem;
|
||||||
|
var dnsServer = new DnsServer4Ray()
|
||||||
|
{
|
||||||
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
||||||
|
skipFallback = true,
|
||||||
|
domains = domainList.ToList(),
|
||||||
|
};
|
||||||
|
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,35 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenInbounds(V2rayConfig v2rayConfig)
|
private void GenInbounds()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var listen = "0.0.0.0";
|
var listen = "0.0.0.0";
|
||||||
v2rayConfig.inbounds = [];
|
_coreConfig.inbounds = [];
|
||||||
|
|
||||||
var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
var inbound = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
||||||
v2rayConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||||
{
|
{
|
||||||
var inbound2 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
var inbound2 = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
||||||
v2rayConfig.inbounds.Add(inbound2);
|
_coreConfig.inbounds.Add(inbound2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Inbound.First().AllowLANConn)
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
{
|
{
|
||||||
if (_config.Inbound.First().NewPort4LAN)
|
if (_config.Inbound.First().NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
|
var inbound3 = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
v2rayConfig.inbounds.Add(inbound3);
|
_coreConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
||||||
{
|
{
|
||||||
inbound3.settings.auth = "password";
|
inbound3.settings.auth = "password";
|
||||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
|
inbound3.settings.accounts = new List<AccountsItem4Ray> { new() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -43,10 +43,9 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
private Inbounds4Ray BuildInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
||||||
{
|
{
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,27 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenLog(V2rayConfig v2rayConfig)
|
private void GenLog()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config.CoreBasicItem.LogEnabled)
|
if (_config.CoreBasicItem.LogEnabled)
|
||||||
{
|
{
|
||||||
var dtNow = DateTime.Now;
|
var dtNow = DateTime.Now;
|
||||||
v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
_coreConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
||||||
v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt");
|
_coreConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt");
|
||||||
v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt");
|
_coreConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
_coreConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
||||||
v2rayConfig.log.access = null;
|
_coreConfig.log.access = null;
|
||||||
v2rayConfig.log.error = null;
|
_coreConfig.log.error = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,94 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound)
|
private void GenOutbounds()
|
||||||
|
{
|
||||||
|
var proxyOutboundList = BuildAllProxyOutbounds();
|
||||||
|
_coreConfig.outbounds.InsertRange(0, proxyOutboundList);
|
||||||
|
if (proxyOutboundList.Count(n => n.tag.StartsWith(Global.ProxyTag)) > 1)
|
||||||
|
{
|
||||||
|
var multipleLoad = _node.GetProtocolExtra().MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||||
|
GenObservatory(multipleLoad);
|
||||||
|
GenBalancer(multipleLoad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Outbounds4Ray> BuildAllProxyOutbounds(string baseTagName = Global.ProxyTag)
|
||||||
|
{
|
||||||
|
var proxyOutboundList = new List<Outbounds4Ray>();
|
||||||
|
if (_node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
proxyOutboundList.AddRange(BuildGroupProxyOutbounds(baseTagName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
proxyOutboundList.Add(BuildProxyOutbound(baseTagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_config.CoreBasicItem.EnableFragment)
|
||||||
|
{
|
||||||
|
var fragmentOutbound = new Outbounds4Ray
|
||||||
|
{
|
||||||
|
protocol = "freedom",
|
||||||
|
tag = $"frag-{Global.ProxyTag}",
|
||||||
|
settings = new()
|
||||||
|
{
|
||||||
|
fragment = new()
|
||||||
|
{
|
||||||
|
packets = _config.Fragment4RayItem?.Packets,
|
||||||
|
length = _config.Fragment4RayItem?.Length,
|
||||||
|
interval = _config.Fragment4RayItem?.Interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var actOutboundWithTlsList =
|
||||||
|
proxyOutboundList.Where(n => n.streamSettings?.security.IsNullOrEmpty() == false
|
||||||
|
&& (n.streamSettings?.sockopt?.dialerProxy?.IsNullOrEmpty() ?? true));
|
||||||
|
foreach (var outbound in actOutboundWithTlsList)
|
||||||
|
{
|
||||||
|
var fragmentOutboundClone = JsonUtils.DeepCopy(fragmentOutbound);
|
||||||
|
fragmentOutboundClone.tag = $"frag-{outbound.tag}";
|
||||||
|
outbound.streamSettings.sockopt = new()
|
||||||
|
{
|
||||||
|
dialerProxy = fragmentOutboundClone.tag
|
||||||
|
};
|
||||||
|
proxyOutboundList.Add(fragmentOutboundClone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proxyOutboundList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Outbounds4Ray> BuildGroupProxyOutbounds(string baseTagName = Global.ProxyTag)
|
||||||
|
{
|
||||||
|
var proxyOutboundList = new List<Outbounds4Ray>();
|
||||||
|
switch (_node.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
proxyOutboundList.AddRange(BuildOutboundsList(baseTagName));
|
||||||
|
break;
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
proxyOutboundList.AddRange(BuildChainOutboundsList(baseTagName));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return proxyOutboundList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Outbounds4Ray BuildProxyOutbound(string baseTagName = Global.ProxyTag)
|
||||||
|
{
|
||||||
|
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
|
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
|
FillOutbound(outbound);
|
||||||
|
outbound.tag = baseTagName;
|
||||||
|
return outbound;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillOutbound(Outbounds4Ray outbound)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var protocolExtra = node.GetProtocolExtra();
|
var protocolExtra = _node.GetProtocolExtra();
|
||||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
var muxEnabled = _node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||||
switch (node.ConfigType)
|
switch (_node.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
{
|
{
|
||||||
|
|
@ -22,8 +103,8 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
vnextItem = outbound.settings.vnext.First();
|
vnextItem = outbound.settings.vnext.First();
|
||||||
}
|
}
|
||||||
vnextItem.address = node.Address;
|
vnextItem.address = _node.Address;
|
||||||
vnextItem.port = node.Port;
|
vnextItem.port = _node.Port;
|
||||||
|
|
||||||
UsersItem4Ray usersItem;
|
UsersItem4Ray usersItem;
|
||||||
if (vnextItem.users.Count <= 0)
|
if (vnextItem.users.Count <= 0)
|
||||||
|
|
@ -36,7 +117,7 @@ public partial class CoreConfigV2rayService
|
||||||
usersItem = vnextItem.users.First();
|
usersItem = vnextItem.users.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
usersItem.id = node.Password;
|
usersItem.id = _node.Password;
|
||||||
usersItem.alterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
|
usersItem.alterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
|
||||||
usersItem.email = Global.UserEMail;
|
usersItem.email = Global.UserEMail;
|
||||||
if (Global.VmessSecurities.Contains(protocolExtra.VmessSecurity))
|
if (Global.VmessSecurities.Contains(protocolExtra.VmessSecurity))
|
||||||
|
|
@ -48,7 +129,7 @@ public partial class CoreConfigV2rayService
|
||||||
usersItem.security = Global.DefaultSecurity;
|
usersItem.security = Global.DefaultSecurity;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
|
FillOutboundMux(outbound, muxEnabled, muxEnabled);
|
||||||
|
|
||||||
outbound.settings.servers = null;
|
outbound.settings.servers = null;
|
||||||
break;
|
break;
|
||||||
|
|
@ -65,17 +146,17 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
serversItem = outbound.settings.servers.First();
|
serversItem = outbound.settings.servers.First();
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = _node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = _node.Port;
|
||||||
serversItem.password = node.Password;
|
serversItem.password = _node.Password;
|
||||||
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(protocolExtra.SsMethod)
|
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod)
|
||||||
? protocolExtra.SsMethod : "none";
|
? protocolExtra.SsMethod : "none";
|
||||||
serversItem.uot = protocolExtra.Uot == true ? true : null;
|
serversItem.uot = protocolExtra.Uot == true ? true : null;
|
||||||
|
|
||||||
serversItem.ota = false;
|
serversItem.ota = false;
|
||||||
serversItem.level = 1;
|
serversItem.level = 1;
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound);
|
FillOutboundMux(outbound);
|
||||||
|
|
||||||
outbound.settings.vnext = null;
|
outbound.settings.vnext = null;
|
||||||
break;
|
break;
|
||||||
|
|
@ -93,25 +174,25 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
serversItem = outbound.settings.servers.First();
|
serversItem = outbound.settings.servers.First();
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = _node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = _node.Port;
|
||||||
serversItem.method = null;
|
serversItem.method = null;
|
||||||
serversItem.password = null;
|
serversItem.password = null;
|
||||||
|
|
||||||
if (node.Username.IsNotEmpty()
|
if (_node.Username.IsNotEmpty()
|
||||||
&& node.Password.IsNotEmpty())
|
&& _node.Password.IsNotEmpty())
|
||||||
{
|
{
|
||||||
SocksUsersItem4Ray socksUsersItem = new()
|
SocksUsersItem4Ray socksUsersItem = new()
|
||||||
{
|
{
|
||||||
user = node.Username ?? "",
|
user = _node.Username ?? "",
|
||||||
pass = node.Password,
|
pass = _node.Password,
|
||||||
level = 1
|
level = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
serversItem.users = new List<SocksUsersItem4Ray>() { socksUsersItem };
|
serversItem.users = new List<SocksUsersItem4Ray>() { socksUsersItem };
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound);
|
FillOutboundMux(outbound);
|
||||||
|
|
||||||
outbound.settings.vnext = null;
|
outbound.settings.vnext = null;
|
||||||
break;
|
break;
|
||||||
|
|
@ -128,8 +209,8 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
vnextItem = outbound.settings.vnext.First();
|
vnextItem = outbound.settings.vnext.First();
|
||||||
}
|
}
|
||||||
vnextItem.address = node.Address;
|
vnextItem.address = _node.Address;
|
||||||
vnextItem.port = node.Port;
|
vnextItem.port = _node.Port;
|
||||||
|
|
||||||
UsersItem4Ray usersItem;
|
UsersItem4Ray usersItem;
|
||||||
if (vnextItem.users.Count <= 0)
|
if (vnextItem.users.Count <= 0)
|
||||||
|
|
@ -141,7 +222,7 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
usersItem = vnextItem.users.First();
|
usersItem = vnextItem.users.First();
|
||||||
}
|
}
|
||||||
usersItem.id = node.Password;
|
usersItem.id = _node.Password;
|
||||||
usersItem.email = Global.UserEMail;
|
usersItem.email = Global.UserEMail;
|
||||||
usersItem.encryption = protocolExtra.VlessEncryption;
|
usersItem.encryption = protocolExtra.VlessEncryption;
|
||||||
|
|
||||||
|
|
@ -151,7 +232,7 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await GenOutboundMux(node, outbound, false, muxEnabled);
|
FillOutboundMux(outbound, false, muxEnabled);
|
||||||
}
|
}
|
||||||
outbound.settings.servers = null;
|
outbound.settings.servers = null;
|
||||||
break;
|
break;
|
||||||
|
|
@ -168,14 +249,14 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
serversItem = outbound.settings.servers.First();
|
serversItem = outbound.settings.servers.First();
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = _node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = _node.Port;
|
||||||
serversItem.password = node.Password;
|
serversItem.password = _node.Password;
|
||||||
|
|
||||||
serversItem.ota = false;
|
serversItem.ota = false;
|
||||||
serversItem.level = 1;
|
serversItem.level = 1;
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound);
|
FillOutboundMux(outbound);
|
||||||
|
|
||||||
outbound.settings.vnext = null;
|
outbound.settings.vnext = null;
|
||||||
break;
|
break;
|
||||||
|
|
@ -185,8 +266,8 @@ public partial class CoreConfigV2rayService
|
||||||
outbound.settings = new()
|
outbound.settings = new()
|
||||||
{
|
{
|
||||||
version = 2,
|
version = 2,
|
||||||
address = node.Address,
|
address = _node.Address,
|
||||||
port = node.Port,
|
port = _node.Port,
|
||||||
};
|
};
|
||||||
outbound.settings.vnext = null;
|
outbound.settings.vnext = null;
|
||||||
outbound.settings.servers = null;
|
outbound.settings.servers = null;
|
||||||
|
|
@ -194,7 +275,7 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
case EConfigType.WireGuard:
|
case EConfigType.WireGuard:
|
||||||
{
|
{
|
||||||
var address = node.Address;
|
var address = _node.Address;
|
||||||
if (Utils.IsIpv6(address))
|
if (Utils.IsIpv6(address))
|
||||||
{
|
{
|
||||||
address = $"[{address}]";
|
address = $"[{address}]";
|
||||||
|
|
@ -202,12 +283,12 @@ public partial class CoreConfigV2rayService
|
||||||
var peer = new WireguardPeer4Ray
|
var peer = new WireguardPeer4Ray
|
||||||
{
|
{
|
||||||
publicKey = protocolExtra.WgPublicKey ?? "",
|
publicKey = protocolExtra.WgPublicKey ?? "",
|
||||||
endpoint = address + ":" + node.Port.ToString()
|
endpoint = address + ":" + _node.Port.ToString()
|
||||||
};
|
};
|
||||||
var setting = new Outboundsettings4Ray
|
var setting = new Outboundsettings4Ray
|
||||||
{
|
{
|
||||||
address = Utils.String2List(protocolExtra.WgInterfaceAddress),
|
address = Utils.String2List(protocolExtra.WgInterfaceAddress),
|
||||||
secretKey = node.Password,
|
secretKey = _node.Password,
|
||||||
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(),
|
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(),
|
||||||
mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(),
|
mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(),
|
||||||
peers = [peer]
|
peers = [peer]
|
||||||
|
|
@ -219,21 +300,20 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outbound.protocol = Global.ProtocolTypes[node.ConfigType];
|
outbound.protocol = Global.ProtocolTypes[_node.ConfigType];
|
||||||
if (node.ConfigType == EConfigType.Hysteria2)
|
if (_node.ConfigType == EConfigType.Hysteria2)
|
||||||
{
|
{
|
||||||
outbound.protocol = "hysteria";
|
outbound.protocol = "hysteria";
|
||||||
}
|
}
|
||||||
await GenBoundStreamSettings(node, outbound);
|
FillBoundStreamSettings(outbound);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabledTCP = false, bool enabledUDP = false)
|
private void FillOutboundMux(Outbounds4Ray outbound, bool enabledTCP = false, bool enabledUDP = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -256,48 +336,40 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenBoundStreamSettings(ProfileItem node, Outbounds4Ray outbound)
|
private void FillBoundStreamSettings(Outbounds4Ray outbound)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var streamSettings = outbound.streamSettings;
|
var streamSettings = outbound.streamSettings;
|
||||||
var network = node.GetNetwork();
|
var network = _node.GetNetwork();
|
||||||
if (node.ConfigType == EConfigType.Hysteria2)
|
if (_node.ConfigType == EConfigType.Hysteria2)
|
||||||
{
|
{
|
||||||
network = "hysteria";
|
network = "hysteria";
|
||||||
}
|
}
|
||||||
streamSettings.network = network;
|
streamSettings.network = network;
|
||||||
var host = node.RequestHost.TrimEx();
|
var host = _node.RequestHost.TrimEx();
|
||||||
var path = node.Path.TrimEx();
|
var path = _node.Path.TrimEx();
|
||||||
var sni = node.Sni.TrimEx();
|
var sni = _node.Sni.TrimEx();
|
||||||
var useragent = "";
|
var useragent = "";
|
||||||
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
|
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
try
|
useragent = Global.UserAgentTexts.GetValueOrDefault(_config.CoreBasicItem.DefUserAgent, _config.CoreBasicItem.DefUserAgent);
|
||||||
{
|
|
||||||
useragent = Global.UserAgentTexts[_config.CoreBasicItem.DefUserAgent];
|
|
||||||
}
|
|
||||||
catch (KeyNotFoundException)
|
|
||||||
{
|
|
||||||
useragent = _config.CoreBasicItem.DefUserAgent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//if tls
|
//if tls
|
||||||
if (node.StreamSecurity == Global.StreamSecurity)
|
if (_node.StreamSecurity == Global.StreamSecurity)
|
||||||
{
|
{
|
||||||
streamSettings.security = node.StreamSecurity;
|
streamSettings.security = _node.StreamSecurity;
|
||||||
|
|
||||||
TlsSettings4Ray tlsSettings = new()
|
TlsSettings4Ray tlsSettings = new()
|
||||||
{
|
{
|
||||||
allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
allowInsecure = Utils.ToBool(_node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : _node.AllowInsecure),
|
||||||
alpn = node.GetAlpn(),
|
alpn = _node.GetAlpn(),
|
||||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint,
|
fingerprint = _node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : _node.Fingerprint,
|
||||||
echConfigList = node.EchConfigList.NullIfEmpty(),
|
echConfigList = _node.EchConfigList.NullIfEmpty(),
|
||||||
echForceQuery = node.EchForceQuery.NullIfEmpty()
|
echForceQuery = _node.EchForceQuery.NullIfEmpty()
|
||||||
};
|
};
|
||||||
if (sni.IsNotEmpty())
|
if (sni.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -307,7 +379,7 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
tlsSettings.serverName = Utils.String2List(host)?.First();
|
tlsSettings.serverName = Utils.String2List(host)?.First();
|
||||||
}
|
}
|
||||||
var certs = CertPemManager.ParsePemChain(node.Cert);
|
var certs = CertPemManager.ParsePemChain(_node.Cert);
|
||||||
if (certs.Count > 0)
|
if (certs.Count > 0)
|
||||||
{
|
{
|
||||||
var certsettings = new List<CertificateSettings4Ray>();
|
var certsettings = new List<CertificateSettings4Ray>();
|
||||||
|
|
@ -324,27 +396,27 @@ public partial class CoreConfigV2rayService
|
||||||
tlsSettings.disableSystemRoot = true;
|
tlsSettings.disableSystemRoot = true;
|
||||||
tlsSettings.allowInsecure = false;
|
tlsSettings.allowInsecure = false;
|
||||||
}
|
}
|
||||||
else if (!node.CertSha.IsNullOrEmpty())
|
else if (!_node.CertSha.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
tlsSettings.pinnedPeerCertSha256 = node.CertSha;
|
tlsSettings.pinnedPeerCertSha256 = _node.CertSha;
|
||||||
tlsSettings.allowInsecure = false;
|
tlsSettings.allowInsecure = false;
|
||||||
}
|
}
|
||||||
streamSettings.tlsSettings = tlsSettings;
|
streamSettings.tlsSettings = tlsSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if Reality
|
//if Reality
|
||||||
if (node.StreamSecurity == Global.StreamSecurityReality)
|
if (_node.StreamSecurity == Global.StreamSecurityReality)
|
||||||
{
|
{
|
||||||
streamSettings.security = node.StreamSecurity;
|
streamSettings.security = _node.StreamSecurity;
|
||||||
|
|
||||||
TlsSettings4Ray realitySettings = new()
|
TlsSettings4Ray realitySettings = new()
|
||||||
{
|
{
|
||||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint,
|
fingerprint = _node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : _node.Fingerprint,
|
||||||
serverName = sni,
|
serverName = sni,
|
||||||
publicKey = node.PublicKey,
|
publicKey = _node.PublicKey,
|
||||||
shortId = node.ShortId,
|
shortId = _node.ShortId,
|
||||||
spiderX = node.SpiderX,
|
spiderX = _node.SpiderX,
|
||||||
mldsa65Verify = node.Mldsa65Verify,
|
mldsa65Verify = _node.Mldsa65Verify,
|
||||||
show = false,
|
show = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -368,14 +440,14 @@ public partial class CoreConfigV2rayService
|
||||||
kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize;
|
kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize;
|
||||||
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
|
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
|
||||||
streamSettings.finalmask ??= new();
|
streamSettings.finalmask ??= new();
|
||||||
if (Global.KcpHeaderMaskMap.TryGetValue(node.HeaderType, out var header))
|
if (Global.KcpHeaderMaskMap.TryGetValue(_node.HeaderType, out var header))
|
||||||
{
|
{
|
||||||
streamSettings.finalmask.udp =
|
streamSettings.finalmask.udp =
|
||||||
[
|
[
|
||||||
new Mask4Ray
|
new Mask4Ray
|
||||||
{
|
{
|
||||||
type = header,
|
type = header,
|
||||||
settings = node.HeaderType == "dns" && !host.IsNullOrEmpty() ? new MaskSettings4Ray { domain = host } : null
|
settings = _node.HeaderType == "dns" && !host.IsNullOrEmpty() ? new MaskSettings4Ray { domain = host } : null
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -445,17 +517,17 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
xhttpSettings.host = host;
|
xhttpSettings.host = host;
|
||||||
}
|
}
|
||||||
if (node.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(node.HeaderType))
|
if (_node.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(_node.HeaderType))
|
||||||
{
|
{
|
||||||
xhttpSettings.mode = node.HeaderType;
|
xhttpSettings.mode = _node.HeaderType;
|
||||||
}
|
}
|
||||||
if (node.Extra.IsNotEmpty())
|
if (_node.Extra.IsNotEmpty())
|
||||||
{
|
{
|
||||||
xhttpSettings.extra = JsonUtils.ParseJson(node.Extra);
|
xhttpSettings.extra = JsonUtils.ParseJson(_node.Extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
streamSettings.xhttpSettings = xhttpSettings;
|
streamSettings.xhttpSettings = xhttpSettings;
|
||||||
await GenOutboundMux(node, outbound);
|
FillOutboundMux(outbound);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
//h2
|
//h2
|
||||||
|
|
@ -479,11 +551,11 @@ public partial class CoreConfigV2rayService
|
||||||
key = path,
|
key = path,
|
||||||
header = new Header4Ray
|
header = new Header4Ray
|
||||||
{
|
{
|
||||||
type = node.HeaderType
|
type = _node.HeaderType
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
streamSettings.quicSettings = quicsettings;
|
streamSettings.quicSettings = quicsettings;
|
||||||
if (node.StreamSecurity == Global.StreamSecurity)
|
if (_node.StreamSecurity == Global.StreamSecurity)
|
||||||
{
|
{
|
||||||
if (sni.IsNotEmpty())
|
if (sni.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -491,7 +563,7 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
streamSettings.tlsSettings.serverName = node.Address;
|
streamSettings.tlsSettings.serverName = _node.Address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -501,7 +573,7 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
authority = host.NullIfEmpty(),
|
authority = host.NullIfEmpty(),
|
||||||
serviceName = path,
|
serviceName = path,
|
||||||
multiMode = node.HeaderType == Global.GrpcMultiMode,
|
multiMode = _node.HeaderType == Global.GrpcMultiMode,
|
||||||
idle_timeout = _config.GrpcItem.IdleTimeout,
|
idle_timeout = _config.GrpcItem.IdleTimeout,
|
||||||
health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
|
health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
|
||||||
permit_without_stream = _config.GrpcItem.PermitWithoutStream,
|
permit_without_stream = _config.GrpcItem.PermitWithoutStream,
|
||||||
|
|
@ -511,7 +583,7 @@ public partial class CoreConfigV2rayService
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
var protocolExtra = node.GetProtocolExtra();
|
var protocolExtra = _node.GetProtocolExtra();
|
||||||
var ports = protocolExtra?.Ports;
|
var ports = protocolExtra?.Ports;
|
||||||
int? upMbps = protocolExtra?.UpMbps is { } su and >= 0
|
int? upMbps = protocolExtra?.UpMbps is { } su and >= 0
|
||||||
? su
|
? su
|
||||||
|
|
@ -537,7 +609,7 @@ public partial class CoreConfigV2rayService
|
||||||
streamSettings.hysteriaSettings = new()
|
streamSettings.hysteriaSettings = new()
|
||||||
{
|
{
|
||||||
version = 2,
|
version = 2,
|
||||||
auth = node.Password,
|
auth = _node.Password,
|
||||||
up = upMbps > 0 ? $"{upMbps}mbps" : null,
|
up = upMbps > 0 ? $"{upMbps}mbps" : null,
|
||||||
down = downMbps > 0 ? $"{downMbps}mbps" : null,
|
down = downMbps > 0 ? $"{downMbps}mbps" : null,
|
||||||
udphop = udpHop,
|
udphop = udpHop,
|
||||||
|
|
@ -558,13 +630,13 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//tcp
|
//tcp
|
||||||
if (node.HeaderType == Global.TcpHeaderHttp)
|
if (_node.HeaderType == Global.TcpHeaderHttp)
|
||||||
{
|
{
|
||||||
TcpSettings4Ray tcpSettings = new()
|
TcpSettings4Ray tcpSettings = new()
|
||||||
{
|
{
|
||||||
header = new Header4Ray
|
header = new Header4Ray
|
||||||
{
|
{
|
||||||
type = node.HeaderType
|
type = _node.HeaderType
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -588,415 +660,142 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<int> GenGroupOutbound(ProfileItem node, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
|
if (!_node.Finalmask.IsNullOrEmpty())
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!node.ConfigType.IsGroupType())
|
|
||||||
{
|
{
|
||||||
return -1;
|
streamSettings.finalmask = JsonUtils.Deserialize<Finalmask4Ray>(_node.Finalmask);
|
||||||
}
|
|
||||||
var hasCycle = await GroupProfileManager.HasCycle(node);
|
|
||||||
if (hasCycle)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
var multipleLoad = profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing;
|
|
||||||
await GenObservatory(v2rayConfig, multipleLoad, baseTagName);
|
|
||||||
await GenBalancer(v2rayConfig, multipleLoad, baseTagName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig)
|
private List<Outbounds4Ray> BuildOutboundsList(string baseTagName = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
//fragment proxy
|
var nodes = new List<ProfileItem>();
|
||||||
if (_config.CoreBasicItem.EnableFragment
|
foreach (var nodeId in Utils.String2List(_node.GetProtocolExtra().ChildItems) ?? [])
|
||||||
&& v2rayConfig.outbounds.First().streamSettings?.security.IsNullOrEmpty() == false)
|
|
||||||
{
|
{
|
||||||
var fragmentOutbound = new Outbounds4Ray
|
if (context.AllProxiesMap.TryGetValue(nodeId, out var node))
|
||||||
{
|
{
|
||||||
protocol = "freedom",
|
nodes.Add(node);
|
||||||
tag = $"frag-{Global.ProxyTag}",
|
|
||||||
settings = new()
|
|
||||||
{
|
|
||||||
fragment = new()
|
|
||||||
{
|
|
||||||
packets = _config.Fragment4RayItem?.Packets,
|
|
||||||
length = _config.Fragment4RayItem?.Length,
|
|
||||||
interval = _config.Fragment4RayItem?.Interval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
v2rayConfig.outbounds.Add(fragmentOutbound);
|
|
||||||
v2rayConfig.outbounds.First().streamSettings.sockopt = new()
|
|
||||||
{
|
|
||||||
dialerProxy = fragmentOutbound.tag
|
|
||||||
};
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.Subid.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
|
||||||
if (subItem is null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//current proxy
|
|
||||||
var outbound = v2rayConfig.outbounds.First();
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
|
||||||
|
|
||||||
//Previous proxy
|
|
||||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
|
||||||
string? prevOutboundTag = null;
|
|
||||||
if (prevNode is not null
|
|
||||||
&& Global.XraySupportConfigType.Contains(prevNode.ConfigType))
|
|
||||||
{
|
|
||||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
|
||||||
await GenOutbound(prevNode, prevOutbound);
|
|
||||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
|
||||||
prevOutbound.tag = prevOutboundTag;
|
|
||||||
v2rayConfig.outbounds.Add(prevOutbound);
|
|
||||||
}
|
|
||||||
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
|
||||||
|
|
||||||
if (nextOutbound is not null)
|
|
||||||
{
|
|
||||||
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<int> GenOutboundsListWithChain(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Get template and initialize list
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
|
||||||
if (txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultOutbounds = new List<Outbounds4Ray>();
|
|
||||||
var prevOutbounds = new List<Outbounds4Ray>(); // Separate list for prev outbounds and fragment
|
|
||||||
|
|
||||||
// Cache for chain proxies to avoid duplicate generation
|
|
||||||
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
|
||||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
|
||||||
var prevIndex = 0; // Index for prev outbounds
|
|
||||||
|
|
||||||
// Process nodes
|
|
||||||
var index = 0;
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node);
|
|
||||||
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);
|
|
||||||
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
|
||||||
if (nextOutbound != null)
|
|
||||||
{
|
|
||||||
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
|
||||||
|
|
||||||
// current proxy
|
|
||||||
await GenOutbound(node, currentOutbound);
|
|
||||||
currentOutbound.tag = $"{baseTagName}-{index}";
|
|
||||||
|
|
||||||
if (!node.Subid.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
|
||||||
{
|
|
||||||
prevTag = value; // maybe null
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
|
||||||
if (prevNode is not null
|
|
||||||
&& Global.XraySupportConfigType.Contains(prevNode.ConfigType))
|
|
||||||
{
|
|
||||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
|
||||||
await GenOutbound(prevNode, prevOutbound);
|
|
||||||
prevTag = $"prev-{baseTagName}-{++prevIndex}";
|
|
||||||
prevOutbound.tag = prevTag;
|
|
||||||
prevOutbounds.Add(prevOutbound);
|
|
||||||
}
|
|
||||||
prevProxyTags[node.Subid] = prevTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
|
||||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
|
||||||
{
|
|
||||||
nextProxyCache[node.Subid] = nextOutbound;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextOutbound is not null)
|
|
||||||
{
|
|
||||||
resultOutbounds.Add(nextOutbound);
|
|
||||||
}
|
|
||||||
resultOutbounds.Add(currentOutbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a chained outbound configuration for the given subItem and outbound.
|
|
||||||
/// The outbound's tag must be set before calling this method.
|
|
||||||
/// Returns the next proxy's outbound configuration, which may be null if no next proxy exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="subItem">The subscription item containing proxy chain information.</param>
|
|
||||||
/// <param name="outbound">The current outbound configuration. Its tag must be set before calling this method.</param>
|
|
||||||
/// <param name="prevOutboundTag">The tag of the previous outbound in the chain, if any.</param>
|
|
||||||
/// <param name="nextOutbound">The outbound for the next proxy in the chain, if already created. If null, will be created inside.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
|
|
||||||
/// </returns>
|
|
||||||
private async Task<Outbounds4Ray?> GenChainOutbounds(SubItem subItem, Outbounds4Ray outbound, string? prevOutboundTag, Outbounds4Ray? nextOutbound = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
|
||||||
|
|
||||||
if (!prevOutboundTag.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
outbound.streamSettings.sockopt = new()
|
|
||||||
{
|
|
||||||
dialerProxy = prevOutboundTag
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next proxy
|
|
||||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
|
||||||
if (nextNode is not null
|
|
||||||
&& Global.XraySupportConfigType.Contains(nextNode.ConfigType))
|
|
||||||
{
|
|
||||||
if (nextOutbound == null)
|
|
||||||
{
|
|
||||||
nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
|
||||||
await GenOutbound(nextNode, nextOutbound);
|
|
||||||
}
|
|
||||||
nextOutbound.tag = outbound.tag;
|
|
||||||
|
|
||||||
outbound.tag = $"mid-{outbound.tag}";
|
|
||||||
nextOutbound.streamSettings.sockopt = new()
|
|
||||||
{
|
|
||||||
dialerProxy = outbound.tag
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return nextOutbound;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
|
||||||
{
|
|
||||||
var resultOutbounds = new List<Outbounds4Ray>();
|
var resultOutbounds = new List<Outbounds4Ray>();
|
||||||
for (var i = 0; i < nodes.Count; i++)
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
{
|
{
|
||||||
var node = nodes[i];
|
var node = nodes[i];
|
||||||
if (node == null)
|
var currentTag = $"{baseTagName}-{i + 1}";
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node);
|
var childProfiles = new CoreConfigV2rayService(context with { Node = node, }).BuildGroupProxyOutbounds(currentTag);
|
||||||
if (childProfiles.Count <= 0)
|
resultOutbounds.AddRange(childProfiles);
|
||||||
{
|
|
||||||
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;
|
continue;
|
||||||
}
|
}
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
var outbound = new CoreConfigV2rayService(context with { Node = node, }).BuildProxyOutbound();
|
||||||
if (txtOutbound.IsNullOrEmpty())
|
outbound.tag = currentTag;
|
||||||
{
|
|
||||||
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);
|
resultOutbounds.Add(outbound);
|
||||||
}
|
}
|
||||||
if (baseTagName == Global.ProxyTag)
|
return resultOutbounds;
|
||||||
{
|
|
||||||
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)
|
private List<Outbounds4Ray> BuildChainOutboundsList(string baseTagName = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
|
var nodes = new List<ProfileItem>();
|
||||||
|
foreach (var nodeId in Utils.String2List(_node.GetProtocolExtra().ChildItems) ?? [])
|
||||||
|
{
|
||||||
|
if (context.AllProxiesMap.TryGetValue(nodeId, out var node))
|
||||||
|
{
|
||||||
|
nodes.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Based on actual network flow instead of data packets
|
// Based on actual network flow instead of data packets
|
||||||
var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
|
var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
|
||||||
var resultOutbounds = new List<Outbounds4Ray>();
|
var resultOutbounds = new List<Outbounds4Ray>();
|
||||||
for (var i = 0; i < nodesReverse.Count; i++)
|
for (var i = 0; i < nodesReverse.Count; i++)
|
||||||
{
|
{
|
||||||
var node = nodesReverse[i];
|
var node = nodesReverse[i];
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
var currentTag = i == 0 ? baseTagName : $"chain-{baseTagName}-{i}";
|
||||||
if (txtOutbound.IsNullOrEmpty())
|
var dialerProxyTag = i != nodesReverse.Count - 1 ? $"chain-{baseTagName}-{i + 1}" : null;
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
break;
|
var childProfiles = new CoreConfigV2rayService(context with { Node = node, }).BuildGroupProxyOutbounds(currentTag);
|
||||||
|
if (!dialerProxyTag.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var chainEndNodes =
|
||||||
|
childProfiles.Where(n => n?.streamSettings?.sockopt?.dialerProxy?.IsNullOrEmpty() ?? true);
|
||||||
|
foreach (var chainEndNode in chainEndNodes)
|
||||||
|
{
|
||||||
|
chainEndNode.streamSettings.sockopt = new()
|
||||||
|
{
|
||||||
|
dialerProxy = dialerProxyTag
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
var chainStartNodes = childProfiles.Where(n => n.tag.StartsWith(currentTag)).ToList();
|
||||||
|
if (chainStartNodes.Count == 1)
|
||||||
|
{
|
||||||
|
foreach (var existedChainEndNode in resultOutbounds.Where(n => n.streamSettings?.sockopt?.dialerProxy == currentTag))
|
||||||
|
{
|
||||||
|
existedChainEndNode.streamSettings.sockopt = new()
|
||||||
|
{
|
||||||
|
dialerProxy = chainStartNodes.First().tag
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (chainStartNodes.Count > 1)
|
||||||
|
{
|
||||||
|
var existedChainNodes = JsonUtils.DeepCopy(resultOutbounds);
|
||||||
|
resultOutbounds.Clear();
|
||||||
|
var j = 0;
|
||||||
|
foreach (var chainStartNode in chainStartNodes)
|
||||||
|
{
|
||||||
|
var existedChainNodesClone = JsonUtils.DeepCopy(existedChainNodes);
|
||||||
|
foreach (var existedChainNode in existedChainNodesClone)
|
||||||
|
{
|
||||||
|
var cloneTag = $"{existedChainNode.tag}-clone-{j + 1}";
|
||||||
|
existedChainNode.tag = cloneTag;
|
||||||
|
}
|
||||||
|
for (var k = 0; k < existedChainNodesClone.Count; k++)
|
||||||
|
{
|
||||||
|
var existedChainNode = existedChainNodesClone[k];
|
||||||
|
var previousDialerProxyTag = existedChainNode.streamSettings?.sockopt?.dialerProxy;
|
||||||
|
var nextTag = k + 1 < existedChainNodesClone.Count
|
||||||
|
? existedChainNodesClone[k + 1].tag
|
||||||
|
: chainStartNode.tag;
|
||||||
|
existedChainNode.streamSettings.sockopt = new()
|
||||||
|
{
|
||||||
|
dialerProxy = (previousDialerProxyTag == currentTag)
|
||||||
|
? chainStartNode.tag
|
||||||
|
: nextTag
|
||||||
|
};
|
||||||
|
resultOutbounds.Add(existedChainNode);
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultOutbounds.AddRange(childProfiles);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var outbound = new CoreConfigV2rayService(context with { Node = node, }).BuildProxyOutbound();
|
||||||
var result = await GenOutbound(node, outbound);
|
|
||||||
|
|
||||||
if (result != 0)
|
outbound.tag = currentTag;
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0)
|
if (!dialerProxyTag.IsNullOrEmpty())
|
||||||
{
|
|
||||||
outbound.tag = baseTagName;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// avoid v2ray observe
|
|
||||||
outbound.tag = "chain-" + baseTagName + i.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i != nodesReverse.Count - 1)
|
|
||||||
{
|
{
|
||||||
outbound.streamSettings.sockopt = new()
|
outbound.streamSettings.sockopt = new()
|
||||||
{
|
{
|
||||||
dialerProxy = "chain-" + baseTagName + (i + 1).ToString()
|
dialerProxy = dialerProxyTag
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
resultOutbounds.Add(outbound);
|
resultOutbounds.Add(outbound);
|
||||||
}
|
}
|
||||||
if (baseTagName == Global.ProxyTag)
|
return resultOutbounds;
|
||||||
{
|
|
||||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
|
||||||
v2rayConfig.outbounds = resultOutbounds;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v2rayConfig.outbounds.AddRange(resultOutbounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,20 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenRouting(V2rayConfig v2rayConfig)
|
private void GenRouting()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (v2rayConfig.routing?.rules != null)
|
if (_coreConfig.routing?.rules != null)
|
||||||
{
|
{
|
||||||
v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
_coreConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (routing != null)
|
if (routing != null)
|
||||||
{
|
{
|
||||||
if (routing.DomainStrategy.IsNotEmpty())
|
if (routing.DomainStrategy.IsNotEmpty())
|
||||||
{
|
{
|
||||||
v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
|
_coreConfig.routing.domainStrategy = routing.DomainStrategy;
|
||||||
}
|
}
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
foreach (var item in rules)
|
foreach (var item in rules)
|
||||||
|
|
@ -31,7 +31,18 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
||||||
await GenRoutingUserRule(item2, v2rayConfig);
|
GenRoutingUserRule(item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var balancerTagList = _coreConfig.routing.balancers
|
||||||
|
?.Select(p => p.tag)
|
||||||
|
.ToList() ?? [];
|
||||||
|
if (balancerTagList.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var rulesItem in _coreConfig.routing.rules.Where(r => balancerTagList.Contains(r.outboundTag + Global.BalancerTagSuffix)))
|
||||||
|
{
|
||||||
|
rulesItem.balancerTag = rulesItem.outboundTag + Global.BalancerTagSuffix;
|
||||||
|
rulesItem.outboundTag = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,95 +51,94 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
|
private void GenRoutingUserRule(RulesItem4Ray? userRule)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (rule == null)
|
if (userRule == null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
rule.outboundTag = await GenRoutingUserRuleOutbound(rule.outboundTag, v2rayConfig);
|
userRule.outboundTag = GenRoutingUserRuleOutbound(userRule.outboundTag ?? Global.ProxyTag);
|
||||||
|
|
||||||
if (rule.port.IsNullOrEmpty())
|
if (userRule.port.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
rule.port = null;
|
userRule.port = null;
|
||||||
}
|
}
|
||||||
if (rule.network.IsNullOrEmpty())
|
if (userRule.network.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
rule.network = null;
|
userRule.network = null;
|
||||||
}
|
}
|
||||||
if (rule.domain?.Count == 0)
|
if (userRule.domain?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.domain = null;
|
userRule.domain = null;
|
||||||
}
|
}
|
||||||
if (rule.ip?.Count == 0)
|
if (userRule.ip?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.ip = null;
|
userRule.ip = null;
|
||||||
}
|
}
|
||||||
if (rule.protocol?.Count == 0)
|
if (userRule.protocol?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.protocol = null;
|
userRule.protocol = null;
|
||||||
}
|
}
|
||||||
if (rule.inboundTag?.Count == 0)
|
if (userRule.inboundTag?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.inboundTag = null;
|
userRule.inboundTag = null;
|
||||||
}
|
}
|
||||||
if (rule.process?.Count == 0)
|
if (userRule.process?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.process = null;
|
userRule.process = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasDomainIp = false;
|
var hasDomainIp = false;
|
||||||
if (rule.domain?.Count > 0)
|
if (userRule.domain?.Count > 0)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.ip = null;
|
it.ip = null;
|
||||||
it.process = null;
|
it.process = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
for (var k = it.domain.Count - 1; k >= 0; k--)
|
for (var k = it.domain.Count - 1; k >= 0; k--)
|
||||||
{
|
{
|
||||||
if (it.domain[k].StartsWith("#"))
|
if (it.domain[k].StartsWith('#'))
|
||||||
{
|
{
|
||||||
it.domain.RemoveAt(k);
|
it.domain.RemoveAt(k);
|
||||||
}
|
}
|
||||||
it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ",");
|
it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ",");
|
||||||
}
|
}
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
hasDomainIp = true;
|
hasDomainIp = true;
|
||||||
}
|
}
|
||||||
if (rule.ip?.Count > 0)
|
if (userRule.ip?.Count > 0)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.domain = null;
|
it.domain = null;
|
||||||
it.process = null;
|
it.process = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
hasDomainIp = true;
|
hasDomainIp = true;
|
||||||
}
|
}
|
||||||
if (rule.process?.Count > 0)
|
if (userRule.process?.Count > 0)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.domain = null;
|
it.domain = null;
|
||||||
it.ip = null;
|
it.ip = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
hasDomainIp = true;
|
hasDomainIp = true;
|
||||||
}
|
}
|
||||||
if (!hasDomainIp)
|
if (!hasDomainIp)
|
||||||
{
|
{
|
||||||
if (rule.port.IsNotEmpty()
|
if (userRule.port.IsNotEmpty()
|
||||||
|| rule.protocol?.Count > 0
|
|| userRule.protocol?.Count > 0
|
||||||
|| rule.inboundTag?.Count > 0
|
|| userRule.inboundTag?.Count > 0
|
||||||
|| rule.network != null
|
|| userRule.network != null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,17 +146,16 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, V2rayConfig v2rayConfig)
|
private string GenRoutingUserRuleOutbound(string outboundTag)
|
||||||
{
|
{
|
||||||
if (Global.OutboundTags.Contains(outboundTag))
|
if (Global.OutboundTags.Contains(outboundTag))
|
||||||
{
|
{
|
||||||
return outboundTag;
|
return outboundTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
var node = context.AllProxiesMap.GetValueOrDefault($"remark:{outboundTag}");
|
||||||
|
|
||||||
if (node == null
|
if (node == null
|
||||||
|| (!Global.XraySupportConfigType.Contains(node.ConfigType)
|
|| (!Global.XraySupportConfigType.Contains(node.ConfigType)
|
||||||
|
|
@ -156,27 +165,20 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
||||||
if (v2rayConfig.outbounds.Any(p => p.tag == tag))
|
if (_coreConfig.outbounds.Any(p => p.tag.StartsWith(tag)))
|
||||||
{
|
{
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
var proxyOutbounds = new CoreConfigV2rayService(context with { Node = node, }).BuildAllProxyOutbounds(tag);
|
||||||
|
_coreConfig.outbounds.AddRange(proxyOutbounds);
|
||||||
|
if (proxyOutbounds.Count(n => n.tag.StartsWith(tag)) > 1)
|
||||||
{
|
{
|
||||||
var ret = await GenGroupOutbound(node, v2rayConfig, tag);
|
var multipleLoad = node.GetProtocolExtra().MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||||
if (ret == 0)
|
GenObservatory(multipleLoad, tag);
|
||||||
{
|
GenBalancer(multipleLoad, tag);
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
return Global.ProxyTag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
return tag;
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
|
||||||
await GenOutbound(node, outbound);
|
|
||||||
outbound.tag = tag;
|
|
||||||
v2rayConfig.outbounds.Add(outbound);
|
|
||||||
|
|
||||||
return outbound.tag;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenStatistic(V2rayConfig v2rayConfig)
|
private void GenStatistic()
|
||||||
{
|
{
|
||||||
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
||||||
{
|
{
|
||||||
|
|
@ -11,17 +11,17 @@ public partial class CoreConfigV2rayService
|
||||||
Policy4Ray policyObj = new();
|
Policy4Ray policyObj = new();
|
||||||
SystemPolicy4Ray policySystemSetting = new();
|
SystemPolicy4Ray policySystemSetting = new();
|
||||||
|
|
||||||
v2rayConfig.stats = new Stats4Ray();
|
_coreConfig.stats = new Stats4Ray();
|
||||||
|
|
||||||
apiObj.tag = tag;
|
apiObj.tag = tag;
|
||||||
v2rayConfig.metrics = apiObj;
|
_coreConfig.metrics = apiObj;
|
||||||
|
|
||||||
policySystemSetting.statsOutboundDownlink = true;
|
policySystemSetting.statsOutboundDownlink = true;
|
||||||
policySystemSetting.statsOutboundUplink = true;
|
policySystemSetting.statsOutboundUplink = true;
|
||||||
policyObj.system = policySystemSetting;
|
policyObj.system = policySystemSetting;
|
||||||
v2rayConfig.policy = policyObj;
|
_coreConfig.policy = policyObj;
|
||||||
|
|
||||||
if (!v2rayConfig.inbounds.Exists(item => item.tag == tag))
|
if (!_coreConfig.inbounds.Exists(item => item.tag == tag))
|
||||||
{
|
{
|
||||||
Inbounds4Ray apiInbound = new();
|
Inbounds4Ray apiInbound = new();
|
||||||
Inboundsettings4Ray apiInboundSettings = new();
|
Inboundsettings4Ray apiInboundSettings = new();
|
||||||
|
|
@ -31,10 +31,10 @@ public partial class CoreConfigV2rayService
|
||||||
apiInbound.protocol = Global.InboundAPIProtocol;
|
apiInbound.protocol = Global.InboundAPIProtocol;
|
||||||
apiInboundSettings.address = Global.Loopback;
|
apiInboundSettings.address = Global.Loopback;
|
||||||
apiInbound.settings = apiInboundSettings;
|
apiInbound.settings = apiInboundSettings;
|
||||||
v2rayConfig.inbounds.Add(apiInbound);
|
_coreConfig.inbounds.Add(apiInbound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag))
|
if (!_coreConfig.routing.rules.Exists(item => item.outboundTag == tag))
|
||||||
{
|
{
|
||||||
RulesItem4Ray apiRoutingRule = new()
|
RulesItem4Ray apiRoutingRule = new()
|
||||||
{
|
{
|
||||||
|
|
@ -43,9 +43,8 @@ public partial class CoreConfigV2rayService
|
||||||
type = "field"
|
type = "field"
|
||||||
};
|
};
|
||||||
|
|
||||||
v2rayConfig.routing.rules.Add(apiRoutingRule);
|
_coreConfig.routing.rules.Add(apiRoutingRule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -262,11 +262,6 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
serverName = SelectedSource.Address;
|
serverName = SelectedSource.Address;
|
||||||
}
|
}
|
||||||
if (!Utils.IsDomain(serverName))
|
|
||||||
{
|
|
||||||
UpdateCertTip(ResUI.ServerNameMustBeValidDomain);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SelectedSource.Port > 0)
|
if (SelectedSource.Port > 0)
|
||||||
{
|
{
|
||||||
domain += $":{SelectedSource.Port}";
|
domain += $":{SelectedSource.Port}";
|
||||||
|
|
@ -292,11 +287,6 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
serverName = SelectedSource.Address;
|
serverName = SelectedSource.Address;
|
||||||
}
|
}
|
||||||
if (!Utils.IsDomain(serverName))
|
|
||||||
{
|
|
||||||
UpdateCertTip(ResUI.ServerNameMustBeValidDomain);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SelectedSource.Port > 0)
|
if (SelectedSource.Port > 0)
|
||||||
{
|
{
|
||||||
domain += $":{SelectedSource.Port}";
|
domain += $":{SelectedSource.Port}";
|
||||||
|
|
|
||||||
|
|
@ -801,7 +801,8 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
|
|
||||||
if (blClipboard)
|
if (blClipboard)
|
||||||
{
|
{
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, item);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(context, null);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(result.Msg);
|
NoticeManager.Instance.Enqueue(result.Msg);
|
||||||
|
|
@ -824,7 +825,8 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(item, fileName);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, item);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(result.Msg);
|
NoticeManager.Instance.Enqueue(result.Msg);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
IsCancel="True" />
|
IsCancel="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
|
@ -807,11 +807,51 @@
|
||||||
Text="{x:Static resx:ResUI.TbPath}" />
|
Text="{x:Static resx:ResUI.TbPath}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Separator Grid.Row="5" Margin="{StaticResource MarginTb8}" />
|
<Grid
|
||||||
|
x:Name="gridFinalmask"
|
||||||
|
Grid.Row="5"
|
||||||
|
ColumnDefinitions="300,Auto">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbFinalmask}" />
|
||||||
|
<Button
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="{StaticResource MarginLr4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Classes="IconButton">
|
||||||
|
<Button.Content>
|
||||||
|
<PathIcon Data="{StaticResource SemiIconMore}">
|
||||||
|
<PathIcon.RenderTransform>
|
||||||
|
<RotateTransform Angle="90" />
|
||||||
|
</PathIcon.RenderTransform>
|
||||||
|
</PathIcon>
|
||||||
|
</Button.Content>
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtFinalmask"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
Classes="TextArea"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
</StackPanel>
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Separator Grid.Row="6" Margin="{StaticResource MarginTb8}" />
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTls"
|
x:Name="gridTls"
|
||||||
Grid.Row="6"
|
Grid.Row="7"
|
||||||
ColumnDefinitions="300,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
|
|
@ -830,7 +870,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTlsMore"
|
x:Name="gridTlsMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
ColumnDefinitions="300,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
@ -993,7 +1033,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridRealityMore"
|
x:Name="gridRealityMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
ColumnDefinitions="300,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
@ -1081,7 +1121,7 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Grid.Row="8" Margin="{StaticResource MarginTb8}" />
|
<Separator Grid.Row="9" Margin="{StaticResource MarginTb8}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.SelectedValue = string.Empty;
|
cmbFingerprint.SelectedValue = string.Empty;
|
||||||
|
gridFinalmask.IsVisible = false;
|
||||||
|
|
||||||
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
||||||
break;
|
break;
|
||||||
|
|
@ -97,11 +98,13 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
gridTransport.IsVisible = false;
|
gridTransport.IsVisible = false;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
|
gridFinalmask.IsVisible = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Naive:
|
case EConfigType.Naive:
|
||||||
gridNaive.IsVisible = true;
|
gridNaive.IsVisible = true;
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
|
gridFinalmask.IsVisible = false;
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.SelectedValue = string.Empty;
|
cmbFingerprint.SelectedValue = string.Empty;
|
||||||
cmbAllowInsecure.IsEnabled = false;
|
cmbAllowInsecure.IsEnabled = false;
|
||||||
|
|
@ -216,6 +219,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid Grid.Row="0">
|
<Grid Grid.Row="0">
|
||||||
|
|
@ -1035,12 +1037,47 @@
|
||||||
Text="{x:Static resx:ResUI.TbPath}" />
|
Text="{x:Static resx:ResUI.TbPath}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Grid x:Name="gridFinalmask" Grid.Row="5">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="300" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbFinalmask}" />
|
||||||
|
<materialDesign:PopupBox
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
StaysOpen="True"
|
||||||
|
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtFinalmask"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
HorizontalScrollBarVisibility="Auto"
|
||||||
|
MaxLines="8"
|
||||||
|
MinLines="4"
|
||||||
|
Style="{StaticResource MyOutlinedTextBox}"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
VerticalScrollBarVisibility="Auto" />
|
||||||
|
</StackPanel>
|
||||||
|
</materialDesign:PopupBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Separator
|
<Separator
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Margin="0,2"
|
Margin="0,2"
|
||||||
Style="{DynamicResource MaterialDesignSeparator}" />
|
Style="{DynamicResource MaterialDesignSeparator}" />
|
||||||
|
|
||||||
<Grid x:Name="gridTls" Grid.Row="6">
|
<Grid x:Name="gridTls" Grid.Row="7">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|
@ -1070,7 +1107,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTlsMore"
|
x:Name="gridTlsMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
Visibility="Hidden">
|
Visibility="Hidden">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|
@ -1259,7 +1296,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridRealityMore"
|
x:Name="gridRealityMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
Visibility="Hidden">
|
Visibility="Hidden">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|
@ -1371,7 +1408,7 @@
|
||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator
|
<Separator
|
||||||
Grid.Row="8"
|
Grid.Row="9"
|
||||||
Margin="0,2"
|
Margin="0,2"
|
||||||
Style="{DynamicResource MaterialDesignSeparator}" />
|
Style="{DynamicResource MaterialDesignSeparator}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ public partial class AddServerWindow
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.Text = string.Empty;
|
cmbFingerprint.Text = string.Empty;
|
||||||
|
gridFinalmask.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
||||||
break;
|
break;
|
||||||
|
|
@ -92,11 +93,13 @@ public partial class AddServerWindow
|
||||||
gridTransport.Visibility = Visibility.Collapsed;
|
gridTransport.Visibility = Visibility.Collapsed;
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
|
gridFinalmask.Visibility = Visibility.Collapsed;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Naive:
|
case EConfigType.Naive:
|
||||||
gridNaive.Visibility = Visibility.Visible;
|
gridNaive.Visibility = Visibility.Visible;
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
|
gridFinalmask.Visibility = Visibility.Collapsed;
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.Text = string.Empty;
|
cmbFingerprint.Text = string.Empty;
|
||||||
cmbAllowInsecure.IsEnabled = false;
|
cmbAllowInsecure.IsEnabled = false;
|
||||||
|
|
@ -212,6 +215,8 @@ public partial class AddServerWindow
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue