mirror of
https://github.com/2dust/v2rayN.git
synced 2026-01-11 00:27:47 +00:00
Add sing-box ech support (#8603)
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
* Add sing-box ech support * Support group config type * Simplified code
This commit is contained in:
parent
4562d4cf00
commit
f3b894015e
6 changed files with 162 additions and 6 deletions
|
|
@ -88,6 +88,7 @@ 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 static readonly List<string> IEProxyProtocols =
|
public static readonly List<string> IEProxyProtocols =
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -316,5 +316,72 @@ public class ProfileGroupItemManager
|
||||||
return childAddresses;
|
return childAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<HashSet<string>> GetAllChildEchQuerySni(string indexId)
|
||||||
|
{
|
||||||
|
// include grand children
|
||||||
|
var childAddresses = new HashSet<string>();
|
||||||
|
if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null)
|
||||||
|
{
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupItem.SubChildItems.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var subItems = await GetSubChildProfileItems(groupItem);
|
||||||
|
foreach (var childNode in subItems)
|
||||||
|
{
|
||||||
|
if (childNode.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (childNode.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& childNode.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = childNode.EchConfigList.IndexOf('+');
|
||||||
|
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childAddresses.Add(childNode.Sni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var childIds = Utils.String2List(groupItem.ChildItems) ?? [];
|
||||||
|
|
||||||
|
foreach (var childId in childIds)
|
||||||
|
{
|
||||||
|
var childNode = await AppManager.Instance.GetProfileItem(childId);
|
||||||
|
if (childNode == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!childNode.IsComplex() && !childNode.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (childNode.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& 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.IndexId);
|
||||||
|
foreach (var addr in subAddresses)
|
||||||
|
{
|
||||||
|
childAddresses.Add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Helper
|
#endregion Helper
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,14 @@ public class Tls4Sbox
|
||||||
public string? fragment_fallback_delay { get; set; }
|
public string? fragment_fallback_delay { get; set; }
|
||||||
public bool? record_fragment { get; set; }
|
public bool? record_fragment { get; set; }
|
||||||
public List<string>? certificate { get; set; }
|
public List<string>? certificate { get; set; }
|
||||||
|
public Ech4Sbox? ech { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Ech4Sbox
|
||||||
|
{
|
||||||
|
public bool enabled { get; set; }
|
||||||
|
public List<string>? config { get; set; }
|
||||||
|
public string? query_server_name { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Multiplex4Sbox
|
public class Multiplex4Sbox
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
await GenRouting(singboxConfig);
|
||||||
await GenExperimental(singboxConfig);
|
await GenExperimental(singboxConfig);
|
||||||
await GenDns(null, singboxConfig);
|
await GenDns(parentNode, singboxConfig);
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|
@ -428,7 +428,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
await GenRouting(singboxConfig);
|
||||||
await GenExperimental(singboxConfig);
|
await GenExperimental(singboxConfig);
|
||||||
await GenDns(null, singboxConfig);
|
await GenDns(parentNode, singboxConfig);
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
var simpleDNSItem = _config.SimpleDNSItem;
|
var simpleDNSItem = _config.SimpleDNSItem;
|
||||||
await GenDnsServers(singboxConfig, simpleDNSItem);
|
await GenDnsServers(node, singboxConfig, simpleDNSItem);
|
||||||
await GenDnsRules(singboxConfig, simpleDNSItem);
|
await GenDnsRules(node, singboxConfig, simpleDNSItem);
|
||||||
|
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.independent_cache = true;
|
singboxConfig.dns.independent_cache = true;
|
||||||
|
|
@ -52,7 +52,7 @@ public partial class CoreConfigSingboxService
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsServers(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private async Task<int> GenDnsServers(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||||
{
|
{
|
||||||
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
||||||
|
|
||||||
|
|
@ -133,6 +133,29 @@ public partial class CoreConfigSingboxService
|
||||||
singboxConfig.dns.servers.Add(fakeip);
|
singboxConfig.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);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,7 +169,7 @@ public partial class CoreConfigSingboxService
|
||||||
return await Task.FromResult(finalDns);
|
return await Task.FromResult(finalDns);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsRules(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private async Task<int> GenDnsRules(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||||
{
|
{
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
||||||
|
|
@ -168,6 +191,31 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var (ech, _) = ParseEchParam(node?.EchConfigList);
|
||||||
|
if (ech is not null)
|
||||||
|
{
|
||||||
|
var echDomain = ech.query_server_name ?? node?.Sni;
|
||||||
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
{
|
||||||
|
query_type = new List<int> { 64, 65 },
|
||||||
|
server = Global.SingboxEchDNSTag,
|
||||||
|
domain = echDomain is not null ? new List<string> { echDomain } : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (node?.ConfigType.IsGroupType() == true)
|
||||||
|
{
|
||||||
|
var queryServerNames = (await ProfileGroupItemManager.GetAllChildEchQuerySni(node.IndexId)).ToList();
|
||||||
|
if (queryServerNames.Count > 0)
|
||||||
|
{
|
||||||
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
{
|
||||||
|
query_type = new List<int> { 64, 65 },
|
||||||
|
server = Global.SingboxEchDNSTag,
|
||||||
|
domain = queryServerNames,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.BlockBindingQuery == true)
|
if (simpleDNSItem.BlockBindingQuery == true)
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
|
|
||||||
|
|
@ -334,6 +334,11 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
tls.insecure = false;
|
tls.insecure = false;
|
||||||
}
|
}
|
||||||
|
var (ech, _) = ParseEchParam(node.EchConfigList);
|
||||||
|
if (ech is not null)
|
||||||
|
{
|
||||||
|
tls.ech = ech;
|
||||||
|
}
|
||||||
outbound.tls = tls;
|
outbound.tls = tls;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -904,4 +909,31 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (Ech4Sbox? ech, Server4Sbox? dnsServer) ParseEchParam(string? echConfig)
|
||||||
|
{
|
||||||
|
if (echConfig.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
if (!echConfig.Contains("://"))
|
||||||
|
{
|
||||||
|
return (new Ech4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
config = [$"-----BEGIN ECH CONFIGS-----\n" +
|
||||||
|
$"{echConfig}\n" +
|
||||||
|
$"-----END ECH CONFIGS-----"],
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
var idx = echConfig.IndexOf('+');
|
||||||
|
// NOTE: query_server_name, since sing-box 1.13.0
|
||||||
|
//var queryServerName = idx > 0 ? echConfig[..idx] : null;
|
||||||
|
var echDnsServer = idx > 0 ? echConfig[(idx + 1)..] : echConfig;
|
||||||
|
return (new Ech4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
query_server_name = null,
|
||||||
|
}, ParseDnsAddress(echDnsServer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue