diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 26619666..b9a369ec 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1233,28 +1233,19 @@ public static class ConfigHandler /// A SOCKS profile item or null if not needed public static ProfileItem? GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType) { + if (node.ConfigType != EConfigType.Custom || !(node.PreSocksPort > 0)) + { + return null; + } ProfileItem? itemSocks = null; - if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun) + var preCoreType = AppManager.Instance.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray; + itemSocks = new ProfileItem() { - itemSocks = new ProfileItem() - { - CoreType = ECoreType.sing_box, - ConfigType = EConfigType.SOCKS, - Address = Global.Loopback, - Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks) - }; - } - else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0) - { - var preCoreType = AppManager.Instance.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray; - itemSocks = new ProfileItem() - { - CoreType = preCoreType, - ConfigType = EConfigType.SOCKS, - Address = Global.Loopback, - Port = node.PreSocksPort.Value, - }; - } + CoreType = preCoreType, + ConfigType = EConfigType.SOCKS, + Address = Global.Loopback, + Port = node.PreSocksPort.Value, + }; return itemSocks; } diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index cebf2d3f..359aab3b 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -154,7 +154,8 @@ public static class CoreConfigHandler IsTunEnabled = config.TunModeItem.EnableTun, SimpleDnsItem = config.SimpleDNSItem, ProtectDomainList = [], - ProtectSocksPort = 0, + TunProtectSsPort = 0, + ProxyRelaySsPort = 0, RawDnsItem = await AppManager.Instance.GetDNSItem(coreType), RoutingItem = await ConfigHandler.GetDefaultRouting(config), }; diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs index 86934c45..0e4c33d6 100644 --- a/v2rayN/ServiceLib/Manager/CoreManager.cs +++ b/v2rayN/ServiceLib/Manager/CoreManager.cs @@ -67,7 +67,38 @@ public class CoreManager var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); 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); if (result.Success != true) { @@ -88,7 +119,7 @@ public class CoreManager } await CoreStart(context); - await CoreStartPreService(context); + await CoreStartPreService(preContext); if (_processService != null) { await UpdateFunc(true, $"{node.GetSummary()}"); @@ -183,30 +214,22 @@ public class CoreManager _processService = proc; } - private async Task CoreStartPreService(CoreConfigContext context) + private async Task CoreStartPreService(CoreConfigContext? preContext) { - var node = context.Node; - if (_processService != null && !_processService.HasExited) + if (_processService is { HasExited: false } && preContext != null) { - var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); - var itemSocks = ConfigHandler.GetPreSocksItem(_config, node, coreType); - if (itemSocks != null) + var preCoreType = preContext?.Node?.CoreType ?? ECoreType.sing_box; + var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); + var result = await CoreConfigHandler.GenerateClientConfig(preContext, fileName); + if (result.Success) { - var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box; - var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); - var itemSocksContext = await CoreConfigHandler.BuildCoreConfigContext(_config, itemSocks); - itemSocksContext.ProtectDomainList.AddRangeSafe(context.ProtectDomainList); - var result = await CoreConfigHandler.GenerateClientConfig(itemSocksContext, fileName); - if (result.Success) + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType); + var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); + if (proc is null) { - var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType); - var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); - if (proc is null) - { - return; - } - _processPreService = proc; + return; } + _processPreService = proc; } } } diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 46caee85..7dc8c266 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -144,7 +144,6 @@ public class TunModeItem public bool StrictRoute { get; set; } = true; public string Stack { get; set; } public int Mtu { get; set; } - public bool EnableExInbound { get; set; } public bool EnableIPv6Address { get; set; } } diff --git a/v2rayN/ServiceLib/Models/CoreConfigContext.cs b/v2rayN/ServiceLib/Models/CoreConfigContext.cs index 9551d39d..59e9537e 100644 --- a/v2rayN/ServiceLib/Models/CoreConfigContext.cs +++ b/v2rayN/ServiceLib/Models/CoreConfigContext.cs @@ -13,5 +13,9 @@ public record CoreConfigContext // TUN Compatibility public bool IsTunEnabled { get; init; } = false; public HashSet 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; } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 6f223693..0f3a45a7 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3771,15 +3771,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 Enable additional Inbound 的本地化字符串。 - /// - public static string TbSettingsEnableExInbound { - get { - return ResourceManager.GetString("TbSettingsEnableExInbound", resourceCulture); - } - } - /// /// 查找类似 Enable fragment 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index dbce6ac4..8a05f2b3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1071,9 +1071,6 @@ MTU - - فعال سازی additional Inbound - فعال سازی آدرس IPv6 diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index 005a51d5..2b92417f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1068,9 +1068,6 @@ MTU - - Activer un port d’écoute supplémentaire - Activer IPv6 diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 3d169590..94711705 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1071,9 +1071,6 @@ MTU - - További bejövő engedélyezése - IPv6 cím engedélyezése diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 041a103b..756a9c93 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1071,9 +1071,6 @@ MTU - - Enable additional Inbound - Enable IPv6 Address diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 66ebef79..51a31851 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1071,9 +1071,6 @@ MTU - - Включить дополнительный входящий канал - Включить IPv6 адреса diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index c8936668..281c81e2 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1068,9 +1068,6 @@ MTU - - 启用额外监听端口 - 启用 IPv6 diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 2ee4dc4f..ce4198ce 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1068,9 +1068,6 @@ MTU - - 啟用額外偵聽連接埠 - 啟用 IPv6 diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs index c0ce7154..1c08fcc5 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs @@ -61,6 +61,51 @@ public partial class CoreConfigSingboxService(CoreConfigContext context) ret.Success = true; 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 { 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; } catch (Exception ex) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs index ab9a9d9f..3a27c7da 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs @@ -7,10 +7,11 @@ public partial class CoreConfigSingboxService try { var listen = "0.0.0.0"; + var listenPort = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); _coreConfig.inbounds = []; - if (!_config.TunModeItem.EnableTun - || (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && AppManager.Instance.RunningCoreType == ECoreType.sing_box)) + if (!context.IsTunEnabled + || (context.IsTunEnabled && _node.Port != listenPort)) { var inbound = new Inbound4Sbox() { @@ -20,7 +21,7 @@ public partial class CoreConfigSingboxService }; _coreConfig.inbounds.Add(inbound); - inbound.listen_port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); + inbound.listen_port = listenPort; 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) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs index 3ad71592..8187f854 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -15,6 +15,10 @@ public partial class CoreConfigV2rayService(CoreConfigContext context) var ret = new RetResult(); try { + if (context.IsTunEnabled && context.TunProtectSsPort > 0 && context.ProxyRelaySsPort > 0) + { + return GenerateClientProxyRelayConfig(); + } if (_node == null || !_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(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 { "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 } diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 86251873..8462a2c3 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -95,7 +95,6 @@ public class OptionSettingViewModel : MyReactiveObject [Reactive] public bool TunStrictRoute { get; set; } [Reactive] public string TunStack { get; set; } [Reactive] public int TunMtu { get; set; } - [Reactive] public bool TunEnableExInbound { get; set; } [Reactive] public bool TunEnableIPv6Address { get; set; } #endregion Tun mode @@ -220,7 +219,6 @@ public class OptionSettingViewModel : MyReactiveObject TunStrictRoute = _config.TunModeItem.StrictRoute; TunStack = _config.TunModeItem.Stack; TunMtu = _config.TunModeItem.Mtu; - TunEnableExInbound = _config.TunModeItem.EnableExInbound; TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address; #endregion Tun mode @@ -380,7 +378,6 @@ public class OptionSettingViewModel : MyReactiveObject _config.TunModeItem.StrictRoute = TunStrictRoute; _config.TunModeItem.Stack = TunStack; _config.TunModeItem.Mtu = TunMtu; - _config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; //coreType diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 65218738..6160cfad 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -843,19 +843,6 @@ Margin="{StaticResource Margin4}" HorizontalAlignment="Left" /> - - - this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 00b2dc9e..ed82860d 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -1097,20 +1097,6 @@ HorizontalAlignment="Left" Style="{StaticResource DefComboBox}" /> - - - vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables);