Tun protect

This commit is contained in:
DHR60 2026-02-08 20:26:46 +08:00
parent aea7078e95
commit b9452b2a6a
6 changed files with 206 additions and 27 deletions

View file

@ -154,7 +154,8 @@ public static class CoreConfigHandler
IsTunEnabled = config.TunModeItem.EnableTun, IsTunEnabled = config.TunModeItem.EnableTun,
SimpleDnsItem = config.SimpleDNSItem, SimpleDnsItem = config.SimpleDNSItem,
ProtectDomainList = [], ProtectDomainList = [],
ProtectSocksPort = 0, TunProtectSsPort = 0,
ProxyRelaySsPort = 0,
RawDnsItem = await AppManager.Instance.GetDNSItem(coreType), RawDnsItem = await AppManager.Instance.GetDNSItem(coreType),
RoutingItem = await ConfigHandler.GetDefaultRouting(config), RoutingItem = await ConfigHandler.GetDefaultRouting(config),
}; };

View file

@ -67,7 +67,38 @@ public class CoreManager
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, node); var context = await CoreConfigHandler.BuildCoreConfigContext(_config, node);
context = context with { IsTunEnabled = _config.TunModeItem.EnableTun }; CoreConfigContext? preContext = null;
if (context.IsTunEnabled)
{
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
if (coreType == ECoreType.Xray && node.ConfigType != EConfigType.Custom)
{
var tunProtectSsPort = Utils.GetFreePort();
var proxyRelaySsPort = Utils.GetFreePort();
context = context with { TunProtectSsPort = tunProtectSsPort, ProxyRelaySsPort = proxyRelaySsPort, };
var preItem = new ProfileItem()
{
CoreType = ECoreType.sing_box,
ConfigType = EConfigType.Shadowsocks,
Address = Global.Loopback,
Port = proxyRelaySsPort,
Password = Global.None,
};
preItem.SetProtocolExtra(preItem.GetProtocolExtra() with
{
SsMethod = Global.None,
});
preContext = context with { Node = preItem, };
}
else
{
var preItem = ConfigHandler.GetPreSocksItem(_config, node, coreType);
if (preItem is not null)
{
preContext = context with { Node = preItem, };
}
}
}
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName); var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
if (result.Success != true) if (result.Success != true)
{ {
@ -88,7 +119,7 @@ public class CoreManager
} }
await CoreStart(context); await CoreStart(context);
await CoreStartPreService(context); await CoreStartPreService(preContext);
if (_processService != null) if (_processService != null)
{ {
await UpdateFunc(true, $"{node.GetSummary()}"); await UpdateFunc(true, $"{node.GetSummary()}");
@ -183,20 +214,13 @@ public class CoreManager
_processService = proc; _processService = proc;
} }
private async Task CoreStartPreService(CoreConfigContext context) private async Task CoreStartPreService(CoreConfigContext? preContext)
{ {
var node = context.Node; if (_processService is { HasExited: false } && preContext != null)
if (_processService != null && !_processService.HasExited)
{ {
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); var preCoreType = preContext?.Node?.CoreType ?? ECoreType.sing_box;
var itemSocks = ConfigHandler.GetPreSocksItem(_config, node, coreType);
if (itemSocks != null)
{
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
var itemSocksContext = await CoreConfigHandler.BuildCoreConfigContext(_config, itemSocks); var result = await CoreConfigHandler.GenerateClientConfig(preContext, fileName);
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);
@ -209,7 +233,6 @@ public class CoreManager
} }
} }
} }
}
private async Task UpdateFunc(bool notify, string msg) private async Task UpdateFunc(bool notify, string msg)
{ {

View file

@ -13,5 +13,9 @@ public record CoreConfigContext
// TUN Compatibility // TUN Compatibility
public bool IsTunEnabled { get; init; } = false; public bool IsTunEnabled { get; init; } = false;
public HashSet<string> ProtectDomainList { get; init; } = new(); public HashSet<string> ProtectDomainList { get; init; } = new();
public int ProtectSocksPort { get; init; } = 0; // -> tun inbound --(if routing proxy)--> relay outbound
// -> proxy core (relay inbound --> proxy outbound --(dialerProxy)--> protect outbound)
// -> protect inbound -> direct proxy outbound data -> internet
public int TunProtectSsPort { get; init; } = 0;
public int ProxyRelaySsPort { get; init; } = 0;
} }

View file

@ -61,6 +61,51 @@ public partial class CoreConfigSingboxService(CoreConfigContext context)
ret.Success = true; ret.Success = true;
ret.Data = ApplyFullConfigTemplate(); ret.Data = ApplyFullConfigTemplate();
if (context.TunProtectSsPort is > 0 and <= 65535)
{
var ssInbound = new
{
type = "shadowsocks",
tag = "tun-protect-ss",
listen = Global.Loopback,
listen_port = context.TunProtectSsPort,
method = "none",
password = "none",
};
var directRule = new Rule4Sbox()
{
inbound = new List<string> { ssInbound.tag },
outbound = Global.DirectTag,
};
var singboxConfigNode = JsonUtils.ParseJson(ret.Data.ToString())!.AsObject();
var inboundsNode = singboxConfigNode["inbounds"]!.AsArray();
inboundsNode.Add(JsonUtils.SerializeToNode(ssInbound, new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}));
var routeNode = singboxConfigNode["route"]?.AsObject();
var rulesNode = routeNode?["rules"]?.AsArray();
var protectRuleNode = JsonUtils.SerializeToNode(directRule,
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
if (rulesNode != null)
{
rulesNode.Insert(0, protectRuleNode);
}
else
{
var newRulesNode = new JsonArray() { protectRuleNode };
if (routeNode is null)
{
var newRouteNode = new JsonObject() { ["rules"] = newRulesNode };
singboxConfigNode["route"] = newRouteNode;
}
else
{
routeNode["rules"] = newRulesNode;
}
}
ret.Data = JsonUtils.Serialize(singboxConfigNode);
}
return ret; return ret;
} }
catch (Exception ex) catch (Exception ex)

View file

@ -7,10 +7,11 @@ public partial class CoreConfigSingboxService
try try
{ {
var listen = "0.0.0.0"; var listen = "0.0.0.0";
var listenPort = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
_coreConfig.inbounds = []; _coreConfig.inbounds = [];
if (!_config.TunModeItem.EnableTun if (!context.IsTunEnabled
|| (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && AppManager.Instance.RunningCoreType == ECoreType.sing_box)) || (context.IsTunEnabled && _node.Port != listenPort))
{ {
var inbound = new Inbound4Sbox() var inbound = new Inbound4Sbox()
{ {
@ -20,7 +21,7 @@ public partial class CoreConfigSingboxService
}; };
_coreConfig.inbounds.Add(inbound); _coreConfig.inbounds.Add(inbound);
inbound.listen_port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); inbound.listen_port = listenPort;
if (_config.Inbound.First().SecondLocalPortEnabled) if (_config.Inbound.First().SecondLocalPortEnabled)
{ {
@ -49,7 +50,7 @@ public partial class CoreConfigSingboxService
} }
} }
if (_config.TunModeItem.EnableTun) if (context.IsTunEnabled)
{ {
if (_config.TunModeItem.Mtu <= 0) if (_config.TunModeItem.Mtu <= 0)
{ {

View file

@ -15,6 +15,10 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
var ret = new RetResult(); var ret = new RetResult();
try try
{ {
if (context.IsTunEnabled && context.TunProtectSsPort > 0 && context.ProxyRelaySsPort > 0)
{
return GenerateClientProxyRelayConfig();
}
if (_node == null if (_node == null
|| !_node.IsValid()) || !_node.IsValid())
{ {
@ -262,5 +266,106 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
} }
} }
public RetResult GenerateClientProxyRelayConfig()
{
var ret = new RetResult();
try
{
if (_node == null
|| !_node.IsValid())
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (_node.GetNetwork() is nameof(ETransport.quic))
{
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
return ret;
}
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
if (result.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
}
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
if (_coreConfig == null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
GenLog();
_coreConfig.outbounds.Clear();
GenOutbounds();
var protectNode = new ProfileItem()
{
CoreType = ECoreType.sing_box,
ConfigType = EConfigType.Shadowsocks,
Address = Global.Loopback,
Port = context.TunProtectSsPort,
Password = Global.None,
};
protectNode.SetProtocolExtra(protectNode.GetProtocolExtra() with
{
SsMethod = Global.None,
});
foreach (var outbound in _coreConfig.outbounds.Where(outbound => outbound.streamSettings?.sockopt?.dialerProxy?.IsNullOrEmpty() ?? true))
{
outbound.streamSettings ??= new StreamSettings4Ray();
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
outbound.streamSettings.sockopt.dialerProxy = "tun-project-ss";
}
_coreConfig.outbounds.Add(new CoreConfigV2rayService(context with
{
Node = protectNode,
}).BuildProxyOutbound("tun-project-ss"));
var hasBalancer = _coreConfig.routing.balancers is { Count: > 0 };
_coreConfig.routing.rules =
[
new()
{
inboundTag = new List<string> { "proxy-relay-ss" },
outboundTag = hasBalancer ? null : Global.ProxyTag,
balancerTag = hasBalancer ? Global.ProxyTag : null,
type = "field"
}
];
_coreConfig.inbounds.Clear();
var configNode = JsonUtils.ParseJson(JsonUtils.Serialize(_coreConfig))!;
configNode["inbounds"]!.AsArray().Add(new
{
listen = Global.Loopback,
port = context.ProxyRelaySsPort,
protocol = "shadowsocks",
settings = new
{
network = "tcp,udp",
method = Global.None,
password = Global.None,
},
tag = "proxy-relay-ss",
});
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
ret.Data = JsonUtils.Serialize(configNode);
return ret;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
#endregion public gen function #endregion public gen function
} }