support Wireguard endpoint

Refactors Singbox config classes for dial fields
DHR60 2025-04-08 21:23:01 +08:00
parent 1e3705b73f
commit 85b7a728ed
2 changed files with 221 additions and 70 deletions

View File

@ -8,6 +8,7 @@ public class SingboxConfig
public Dns4Sbox? dns { get; set; } public Dns4Sbox? dns { get; set; }
public List<Inbound4Sbox> inbounds { get; set; } public List<Inbound4Sbox> inbounds { get; set; }
public List<Outbound4Sbox> outbounds { get; set; } public List<Outbound4Sbox> outbounds { get; set; }
public List<Endpoints4Sbox>? endpoints { get; set; }
public Route4Sbox route { get; set; } public Route4Sbox route { get; set; }
public Experimental4Sbox? experimental { get; set; } public Experimental4Sbox? experimental { get; set; }
} }
@ -96,10 +97,8 @@ public class User4Sbox
public string password { get; set; } public string password { get; set; }
} }
public class Outbound4Sbox public class Outbound4Sbox : BaseServer4Sbox
{ {
public string type { get; set; }
public string tag { get; set; }
public string? server { get; set; } public string? server { get; set; }
public int? server_port { get; set; } public int? server_port { get; set; }
public List<string>? server_ports { get; set; } public List<string>? server_ports { get; set; }
@ -114,7 +113,6 @@ public class Outbound4Sbox
public int? recv_window_conn { get; set; } public int? recv_window_conn { get; set; }
public int? recv_window { get; set; } public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; } public bool? disable_mtu_discovery { get; set; }
public string? detour { get; set; }
public string? method { get; set; } public string? method { get; set; }
public string? username { get; set; } public string? username { get; set; }
public string? password { get; set; } public string? password { get; set; }
@ -122,26 +120,14 @@ public class Outbound4Sbox
public string? version { get; set; } public string? version { get; set; }
public string? network { get; set; } public string? network { get; set; }
public string? packet_encoding { get; set; } public string? packet_encoding { get; set; }
public List<string>? local_address { get; set; }
public string? private_key { get; set; }
public string? peer_public_key { get; set; }
public List<int>? reserved { get; set; }
public int? mtu { get; set; }
public string? plugin { get; set; } public string? plugin { get; set; }
public string? plugin_opts { get; set; } public string? plugin_opts { get; set; }
public Tls4Sbox? tls { get; set; }
public Multiplex4Sbox? multiplex { get; set; }
public Transport4Sbox? transport { get; set; }
public HyObfs4Sbox? obfs { get; set; }
public List<string>? outbounds { get; set; } public List<string>? outbounds { get; set; }
public bool? interrupt_exist_connections { get; set; } public bool? interrupt_exist_connections { get; set; }
public Rule4Sbox? domain_resolver { get; set; }
} }
public class Endpoints4Sbox public class Endpoints4Sbox : BaseServer4Sbox
{ {
public string type { get; set; }
public string tag { get; set; }
public bool? system { get; set; } public bool? system { get; set; }
public string? name { get; set; } public string? name { get; set; }
public int? mtu { get; set; } public int? mtu { get; set; }
@ -219,14 +205,11 @@ public class HyObfs4Sbox
public string? password { get; set; } public string? password { get; set; }
} }
public class Server4Sbox public class Server4Sbox : BaseServer4Sbox
{ {
public string? tag { get; set; }
public string? detour { get; set; }
public string? inet4_range { get; set; } public string? inet4_range { get; set; }
public string? inet6_range { get; set; } public string? inet6_range { get; set; }
public string? client_subnet { get; set; } public string? client_subnet { get; set; }
public string? type { get; set; }
public string? server { get; set; } public string? server { get; set; }
public string? server_resolver { get; set; } public string? server_resolver { get; set; }
[JsonPropertyName("interface")] public string? Interface { get; set; } [JsonPropertyName("interface")] public string? Interface { get; set; }
@ -277,3 +260,33 @@ public class Ruleset4Sbox
public string? download_detour { get; set; } public string? download_detour { get; set; }
public string? update_interval { get; set; } public string? update_interval { get; set; }
} }
public abstract class DialFields4Sbox
{
public string? detour { get; set; }
public string? bind_interface { get; set; }
public string? inet4_bind_address { get; set; }
public string? inet6_bind_address { get; set; }
public int? routing_mark { get; set; }
public bool? reuse_addr { get; set; }
public string? netns { get; set; }
public string? connect_timeout { get; set; }
public bool? tcp_fast_open { get; set; }
public bool? tcp_multi_path { get; set; }
public bool? udp_fragment { get; set; }
public Rule4Sbox? domain_resolver { get; set; } // or string
public string? network_strategy { get; set; }
public List<string>? network_type { get; set; }
public List<string>? fallback_network_type { get; set; }
public string? fallback_delay { get; set; }
public Tls4Sbox? tls { get; set; }
public Multiplex4Sbox? multiplex { get; set; }
public Transport4Sbox? transport { get; set; }
public HyObfs4Sbox? obfs { get; set; }
}
public abstract class BaseServer4Sbox : DialFields4Sbox
{
public string type { get; set; }
public string tag { get; set; }
}

View File

@ -1,6 +1,7 @@
using System.Data; using System.Data;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Reactive;
using DynamicData; using DynamicData;
using ServiceLib.Models; using ServiceLib.Models;
@ -55,7 +56,18 @@ public class CoreConfigSingboxService
await GenInbounds(singboxConfig); await GenInbounds(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds.First()); if (node.ConfigType == EConfigType.WireGuard)
{
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); await GenMoreOutbounds(node, singboxConfig);
@ -204,16 +216,29 @@ public class CoreConfigSingboxService
continue; continue;
} }
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); var server = await GenServer(item);
await GenOutbound(item, outbound); if (server is null)
outbound.tag = Global.ProxyTag + inbound.listen_port.ToString(); {
singboxConfig.outbounds.Add(outbound); ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
var tag = Global.ProxyTag + inbound.listen_port.ToString();
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);
}
//rule //rule
Rule4Sbox rule = new() Rule4Sbox rule = new()
{ {
inbound = new List<string> { inbound.tag }, inbound = new List<string> { inbound.tag },
outbound = outbound.tag outbound = tag
}; };
singboxConfig.route.rules.Add(rule); singboxConfig.route.rules.Add(rule);
} }
@ -277,7 +302,18 @@ public class CoreConfigSingboxService
} }
await GenLog(singboxConfig); await GenLog(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds.First()); if (node.ConfigType == EConfigType.WireGuard)
{
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); await GenMoreOutbounds(node, singboxConfig);
await GenDnsDomains(null, singboxConfig, null); await GenDnsDomains(null, singboxConfig, null);
@ -731,15 +767,6 @@ public class CoreConfigSingboxService
outbound.congestion_control = node.HeaderType; outbound.congestion_control = node.HeaderType;
break; break;
} }
case EConfigType.WireGuard:
{
outbound.private_key = node.Id;
outbound.peer_public_key = node.PublicKey;
outbound.reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList();
outbound.local_address = Utils.String2List(node.RequestHost);
outbound.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt();
break;
}
case EConfigType.Anytls: case EConfigType.Anytls:
{ {
outbound.password = node.Id; outbound.password = node.Id;
@ -758,6 +785,76 @@ public class CoreConfigSingboxService
return 0; return 0;
} }
private async Task<int> GenEndpoint(ProfileItem node, Endpoints4Sbox endpoint)
{
try
{
endpoint.address = Utils.String2List(node.RequestHost);
// Utils.GetFreePort() 9090 ?
endpoint.listen_port = Utils.GetFreePort();
endpoint.type = Global.ProtocolTypes[node.ConfigType];
if (Utils.IsDomain(node.Address))
{
var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
endpoint.domain_resolver = new()
{
server = "local_local",
strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom
};
}
switch (node.ConfigType)
{
case EConfigType.WireGuard:
{
var peer = new Peer4Sbox
{
public_key = node.PublicKey,
reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(),
address = node.Address,
port = node.Port,
// TODO default ["0.0.0.0/0", "::/0"]
allowed_ips = new() { "0.0.0.0/0", "::/0" },
};
endpoint.private_key = node.Id;
endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt();
break;
}
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return await Task.FromResult(0);
}
private async Task<BaseServer4Sbox?> GenServer(ProfileItem node)
{
try
{
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
if (node.ConfigType == EConfigType.WireGuard)
{
var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound);
await GenEndpoint(node, endpoint);
return endpoint;
}
else
{
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(node, outbound);
return outbound;
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return await Task.FromResult<BaseServer4Sbox?>(null);
}
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
@ -924,7 +1021,8 @@ public class CoreConfigSingboxService
} }
//current proxy //current proxy
var outbound = singboxConfig.outbounds.First(); BaseServer4Sbox? outbound = singboxConfig.endpoints?.FirstOrDefault(t => t.tag == Global.ProxyTag) == null ? singboxConfig.outbounds.First() : null;
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
//Previous proxy //Previous proxy
@ -933,17 +1031,32 @@ public class CoreConfigSingboxService
if (prevNode is not null if (prevNode is not null
&& prevNode.ConfigType != EConfigType.Custom) && prevNode.ConfigType != EConfigType.Custom)
{ {
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(prevNode, prevOutbound);
prevOutboundTag = $"prev-{Global.ProxyTag}"; prevOutboundTag = $"prev-{Global.ProxyTag}";
prevOutbound.tag = prevOutboundTag; var prevServer = await GenServer(prevNode);
singboxConfig.outbounds.Add(prevOutbound); prevServer.tag = prevOutboundTag;
if (prevServer is Endpoints4Sbox endpoint)
{
singboxConfig.endpoints ??= new();
singboxConfig.endpoints.Add(endpoint);
}
else if (prevServer is Outbound4Sbox outboundPrev)
{
singboxConfig.outbounds.Add(outboundPrev);
}
} }
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag); var nextServer = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
if (nextOutbound is not null) if (nextServer is not null)
{ {
singboxConfig.outbounds.Insert(0, nextOutbound); if (nextServer is Endpoints4Sbox endpoint)
{
singboxConfig.endpoints ??= new();
singboxConfig.endpoints.Insert(0, endpoint);
}
else if (nextServer is Outbound4Sbox outboundNext)
{
singboxConfig.outbounds.Insert(0, outboundNext);
}
} }
} }
catch (Exception ex) catch (Exception ex)
@ -966,11 +1079,13 @@ public class CoreConfigSingboxService
} }
var resultOutbounds = new List<Outbound4Sbox>(); var resultOutbounds = new List<Outbound4Sbox>();
var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints
var prevOutbounds = new List<Outbound4Sbox>(); // Separate list for prev outbounds var prevOutbounds = new List<Outbound4Sbox>(); // Separate list for prev outbounds
var prevEndpoints = new List<Endpoints4Sbox>(); // Separate list for prev endpoints
var proxyTags = new List<string>(); // For selector and urltest outbounds var proxyTags = new List<string>(); // For selector and urltest outbounds
// Cache for chain proxies to avoid duplicate generation // Cache for chain proxies to avoid duplicate generation
var nextProxyCache = new Dictionary<string, Outbound4Sbox?>(); var nextProxyCache = new Dictionary<string, BaseServer4Sbox?>();
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
int prevIndex = 0; // Index for prev outbounds int prevIndex = 0; // Index for prev outbounds
@ -982,19 +1097,18 @@ public class CoreConfigSingboxService
// Handle proxy chain // Handle proxy chain
string? prevTag = null; string? prevTag = null;
var currentOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); var currentServer = await GenServer(node);
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null); var nextServer = nextProxyCache.GetValueOrDefault(node.Subid, null);
if (nextOutbound != null) if (nextServer != null)
{ {
nextOutbound = JsonUtils.DeepCopy(nextOutbound); nextServer = JsonUtils.DeepCopy(nextServer);
} }
var subItem = await AppHandler.Instance.GetSubItem(node.Subid); var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
// current proxy // current proxy
await GenOutbound(node, currentOutbound); currentServer.tag = $"{Global.ProxyTag}-{index}";
currentOutbound.tag = $"{Global.ProxyTag}-{index}"; proxyTags.Add(currentServer.tag);
proxyTags.Add(currentOutbound.tag);
if (!node.Subid.IsNullOrEmpty()) if (!node.Subid.IsNullOrEmpty())
{ {
@ -1017,18 +1131,32 @@ public class CoreConfigSingboxService
prevProxyTags[node.Subid] = prevTag; prevProxyTags[node.Subid] = prevTag;
} }
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound); nextServer = await GenChainOutbounds(subItem, currentServer, prevTag, nextServer);
if (!nextProxyCache.ContainsKey(node.Subid)) if (!nextProxyCache.ContainsKey(node.Subid))
{ {
nextProxyCache[node.Subid] = nextOutbound; nextProxyCache[node.Subid] = nextServer;
} }
} }
if (nextOutbound is not null) if (nextServer is not null)
{ {
resultOutbounds.Add(nextOutbound); if (nextServer is Endpoints4Sbox nextEndpoint)
{
resultEndpoints.Add(nextEndpoint);
}
else if (nextServer is Outbound4Sbox nextOutbound)
{
resultOutbounds.Add(nextOutbound);
}
}
if (currentServer is Endpoints4Sbox currentEndpoint)
{
resultEndpoints.Add(currentEndpoint);
}
else if (currentServer is Outbound4Sbox currentOutbound)
{
resultOutbounds.Add(currentOutbound);
} }
resultOutbounds.Add(currentOutbound);
} }
// Add urltest outbound (auto selection based on latency) // Add urltest outbound (auto selection based on latency)
@ -1061,6 +1189,9 @@ public class CoreConfigSingboxService
resultOutbounds.AddRange(prevOutbounds); resultOutbounds.AddRange(prevOutbounds);
resultOutbounds.AddRange(singboxConfig.outbounds); resultOutbounds.AddRange(singboxConfig.outbounds);
singboxConfig.outbounds = resultOutbounds; singboxConfig.outbounds = resultOutbounds;
singboxConfig.endpoints ??= new List<Endpoints4Sbox>();
resultEndpoints.AddRange(singboxConfig.endpoints);
singboxConfig.endpoints = resultEndpoints;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1082,7 +1213,7 @@ public class CoreConfigSingboxService
/// <returns> /// <returns>
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists. /// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
/// </returns> /// </returns>
private async Task<Outbound4Sbox?> GenChainOutbounds(SubItem subItem, Outbound4Sbox outbound, string? prevOutboundTag, Outbound4Sbox? nextOutbound = null) private async Task<BaseServer4Sbox?> GenChainOutbounds(SubItem subItem, BaseServer4Sbox outbound, string? prevOutboundTag, BaseServer4Sbox? nextOutbound = null)
{ {
try try
{ {
@ -1098,11 +1229,7 @@ public class CoreConfigSingboxService
if (nextNode is not null if (nextNode is not null
&& nextNode.ConfigType != EConfigType.Custom) && nextNode.ConfigType != EConfigType.Custom)
{ {
if (nextOutbound == null) nextOutbound ??= await GenServer(nextNode);
{
nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(nextNode, nextOutbound);
}
nextOutbound.tag = outbound.tag; nextOutbound.tag = outbound.tag;
outbound.tag = $"mid-{outbound.tag}"; outbound.tag = $"mid-{outbound.tag}";
@ -1426,13 +1553,24 @@ public class CoreConfigSingboxService
return Global.ProxyTag; return Global.ProxyTag;
} }
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); var server = await GenServer(node);
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); if (server is null)
await GenOutbound(node, outbound); {
outbound.tag = Global.ProxyTag + node.IndexId.ToString(); return Global.ProxyTag;
singboxConfig.outbounds.Add(outbound); }
return outbound.tag; server.tag = Global.ProxyTag + node.IndexId.ToString();
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;
} }
private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig) private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig)