From 77a4c894b306a8e2846b4592fdad17adbafef1df Mon Sep 17 00:00:00 2001 From: nirvanalinlei Date: Mon, 30 Mar 2026 14:04:06 +0800 Subject: [PATCH 1/3] Fix sing-box selector generation for dynamic group tags --- .../Services/CoreConfig/Singbox/SingboxOutboundService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 0fc9733c..7a86761f 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -22,7 +22,7 @@ public partial class CoreConfigSingboxService } if (withSelector) { - var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList(); + var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(baseTagName)).Select(n => n.tag).ToList(); if (proxyTags.Count > 1) { proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName)); From 935d9e0aa3047cc6490b35979bcf33da508f1a48 Mon Sep 17 00:00:00 2001 From: nirvanalinlei Date: Mon, 30 Mar 2026 23:08:47 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20TUN=20=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=20DNS=20=E5=A4=B1=E8=B4=A5=E5=8F=8A=20Window?= =?UTF-8?q?s=20=E6=8F=90=E6=9D=83=E9=87=8D=E5=90=AF=E5=90=8E=20TUN=20?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2rayN/ServiceLib/Common/ProcUtils.cs | 5 ++- v2rayN/ServiceLib/Manager/AppManager.cs | 10 ++++- .../Singbox/SingboxOutboundService.cs | 23 +++++++++++ .../ViewModels/StatusBarViewModel.cs | 39 ++++++++++++------- 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/v2rayN/ServiceLib/Common/ProcUtils.cs b/v2rayN/ServiceLib/Common/ProcUtils.cs index ce487c7a..3f5c31af 100644 --- a/v2rayN/ServiceLib/Common/ProcUtils.cs +++ b/v2rayN/ServiceLib/Common/ProcUtils.cs @@ -46,7 +46,7 @@ public static class ProcUtils return null; } - public static void RebootAsAdmin(bool blAdmin = true) + public static bool RebootAsAdmin(bool blAdmin = true) { try { @@ -58,11 +58,12 @@ public static class ProcUtils FileName = Utils.GetExePath().AppendQuotes(), Verb = blAdmin ? "runas" : null, }; - _ = Process.Start(startInfo); + return Process.Start(startInfo) != null; } catch (Exception ex) { Logging.SaveLog(_tag, ex); + return false; } } } diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 35847d71..7354c305 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -144,10 +144,16 @@ public sealed class AppManager AppEvents.ShutdownRequested.Publish(byUser); } - public async Task RebootAsAdmin() + public async Task RebootAsAdmin() { - ProcUtils.RebootAsAdmin(); + var started = ProcUtils.RebootAsAdmin(); + if (!started) + { + return false; + } + await AppManager.Instance.AppExitAsync(true); + return true; } #endregion App diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 7a86761f..f422da96 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -345,6 +345,14 @@ public partial class CoreConfigSingboxService { try { + // The synthetic TUN relay outbound talks to the local Xray shadowsocks relay. + // Xray cannot terminate sing-box h2mux, so muxing here turns local relay traffic + // into sp.mux.sing-box.arpa pseudo-destinations and breaks DNS over TUN. + if (IsTunRelayProxyOutbound()) + { + return; + } + var muxEnabled = _node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled; if (muxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty()) { @@ -364,6 +372,21 @@ public partial class CoreConfigSingboxService } } + private bool IsTunRelayProxyOutbound() + { + if (!context.IsTunEnabled + || _node.ConfigType != EConfigType.Shadowsocks + || _node.Address != Global.Loopback + || _node.Port != context.ProxyRelaySsPort + || _node.Password != Global.None) + { + return false; + } + + var protocolExtra = _node.GetProtocolExtra(); + return protocolExtra.SsMethod == Global.None; + } + private void FillOutboundTls(Outbound4Sbox outbound) { try diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index b8154c16..aac51c5c 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -469,31 +469,40 @@ public class StatusBarViewModel : MyReactiveObject return; } - _config.TunModeItem.EnableTun = EnableTun; - - if (EnableTun && AllowEnableTun() == false) + var allowEnableTun = AllowEnableTun(); + if (PrepareTunConfigForPrivilegeEscalation(_config, EnableTun, allowEnableTun, Utils.IsWindows())) { - // When running as a non-administrator, reboot to administrator mode - if (Utils.IsWindows()) + await ConfigHandler.SaveConfig(_config); + if (!await AppManager.Instance.RebootAsAdmin()) + { + _config.TunModeItem.EnableTun = false; + EnableTun = false; + await ConfigHandler.SaveConfig(_config); + NoticeManager.Instance.SendMessageEx(ResUI.OperationFailed); + } + return; + } + + if (EnableTun && !allowEnableTun) + { + bool? passwordResult = await _updateView?.Invoke(EViewAction.PasswordInput, null); + if (passwordResult == false) { _config.TunModeItem.EnableTun = false; - await AppManager.Instance.RebootAsAdmin(); return; } - else - { - bool? passwordResult = await _updateView?.Invoke(EViewAction.PasswordInput, null); - if (passwordResult == false) - { - _config.TunModeItem.EnableTun = false; - return; - } - } } + await ConfigHandler.SaveConfig(_config); AppEvents.ReloadRequested.Publish(); } + private static bool PrepareTunConfigForPrivilegeEscalation(Config config, bool enableTun, bool allowEnableTun, bool isWindows) + { + config.TunModeItem.EnableTun = enableTun; + return enableTun && !allowEnableTun && isWindows; + } + private bool AllowEnableTun() { if (Utils.IsWindows()) From 4521e022aaad2479a29294946b516db72690d2f8 Mon Sep 17 00:00:00 2001 From: nirvanalinlei Date: Thu, 2 Apr 2026 04:29:06 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=8C=89=E4=B8=8A=E6=B8=B8=E6=84=8F?= =?UTF-8?q?=E8=A7=81=E5=9B=9E=E6=BB=9A=20TUN=20=E6=8F=90=E6=9D=83=E6=97=B6?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E5=90=AF=E7=94=A8=E7=8A=B6=E6=80=81=E7=9A=84?= =?UTF-8?q?=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2rayN/ServiceLib/Manager/AppManager.cs | 10 ++---- .../ViewModels/StatusBarViewModel.cs | 36 ++++++++----------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 7354c305..35847d71 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -144,16 +144,10 @@ public sealed class AppManager AppEvents.ShutdownRequested.Publish(byUser); } - public async Task RebootAsAdmin() + public async Task RebootAsAdmin() { - var started = ProcUtils.RebootAsAdmin(); - if (!started) - { - return false; - } - + ProcUtils.RebootAsAdmin(); await AppManager.Instance.AppExitAsync(true); - return true; } #endregion App diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index aac51c5c..0ce40dbb 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -469,40 +469,32 @@ public class StatusBarViewModel : MyReactiveObject return; } - var allowEnableTun = AllowEnableTun(); - if (PrepareTunConfigForPrivilegeEscalation(_config, EnableTun, allowEnableTun, Utils.IsWindows())) - { - await ConfigHandler.SaveConfig(_config); - if (!await AppManager.Instance.RebootAsAdmin()) - { - _config.TunModeItem.EnableTun = false; - EnableTun = false; - await ConfigHandler.SaveConfig(_config); - NoticeManager.Instance.SendMessageEx(ResUI.OperationFailed); - } - return; - } + _config.TunModeItem.EnableTun = EnableTun; - if (EnableTun && !allowEnableTun) + if (EnableTun && AllowEnableTun() == false) { - bool? passwordResult = await _updateView?.Invoke(EViewAction.PasswordInput, null); - if (passwordResult == false) + // When running as a non-administrator, reboot to administrator mode + if (Utils.IsWindows()) { _config.TunModeItem.EnableTun = false; + await AppManager.Instance.RebootAsAdmin(); return; } + else + { + bool? passwordResult = await _updateView?.Invoke(EViewAction.PasswordInput, null); + if (passwordResult == false) + { + _config.TunModeItem.EnableTun = false; + return; + } + } } await ConfigHandler.SaveConfig(_config); AppEvents.ReloadRequested.Publish(); } - private static bool PrepareTunConfigForPrivilegeEscalation(Config config, bool enableTun, bool allowEnableTun, bool isWindows) - { - config.TunModeItem.EnableTun = enableTun; - return enableTun && !allowEnableTun && isWindows; - } - private bool AllowEnableTun() { if (Utils.IsWindows())