diff --git a/v2rayN/ServiceLib/Handler/ConnectionHandler.cs b/v2rayN/ServiceLib/Handler/ConnectionHandler.cs index 05825d93..896d0947 100644 --- a/v2rayN/ServiceLib/Handler/ConnectionHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConnectionHandler.cs @@ -46,6 +46,7 @@ public static class ConnectionHandler { var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{port}"); + ApplyMixedPortAuth(webProxy); var url = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl; for (var i = 0; i < 2; i++) @@ -95,4 +96,20 @@ public static class ConnectionHandler } return responseTime; } + + private static void ApplyMixedPortAuth(WebProxy webProxy) + { + var inbound = AppManager.Instance.Config?.Inbound?.FirstOrDefault(); + if (inbound?.MixedPortAuthEnabled != true) + { + return; + } + if (inbound.MixedPortAuthUser.IsNullOrEmpty() || inbound.MixedPortAuthPass.IsNullOrEmpty()) + { + return; + } + + // Use credential object instead of URI userinfo to avoid leaking secrets. + webProxy.Credentials = new NetworkCredential(inbound.MixedPortAuthUser, inbound.MixedPortAuthPass); + } } diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index fa766f0d..98b4bae4 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -35,6 +35,9 @@ public class InItem public bool NewPort4LAN { get; set; } public string User { get; set; } public string Pass { get; set; } + public bool MixedPortAuthEnabled { get; set; } + public string MixedPortAuthUser { get; set; } + public string MixedPortAuthPass { get; set; } public bool SecondLocalPortEnabled { get; set; } } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index d323889a..547888f2 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -4068,6 +4068,33 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Enable Mixed Port Auth 的本地化字符串。 + /// + public static string TbSettingsMixedPortAuthEnabled { + get { + return ResourceManager.GetString("TbSettingsMixedPortAuthEnabled", resourceCulture); + } + } + + /// + /// 查找类似 Mixed Port Auth Pass 的本地化字符串。 + /// + public static string TbSettingsMixedPortAuthPass { + get { + return ResourceManager.GetString("TbSettingsMixedPortAuthPass", resourceCulture); + } + } + + /// + /// 查找类似 Mixed Port Auth User 的本地化字符串。 + /// + public static string TbSettingsMixedPortAuthUser { + get { + return ResourceManager.GetString("TbSettingsMixedPortAuthUser", resourceCulture); + } + } + /// /// 查找类似 sing-box Mux Protocol 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index eda45653..694c5bd3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1338,6 +1338,15 @@ نوع Sniffing + + فعال‌سازی احراز هویت پورت ترکیبی + + + رمز عبور احراز هویت پورت ترکیبی + + + نام کاربری احراز هویت پورت ترکیبی + فعال کردن دومین پورت ترکیبی diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index 8f173e6e..d64a5cae 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1335,6 +1335,15 @@ Type de détection de trafic + + Activer l'authentification du port mixte + + + Mot de passe d'authentification du port mixte + + + Utilisateur d'authentification du port mixte + Activer un second port d’écoute local diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 4437eafa..69050aae 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1338,6 +1338,15 @@ Sniffing típus + + Vegyes port hitelesítés engedélyezése + + + Vegyes port hitelesítési jelszó + + + Vegyes port hitelesítési felhasználó + Második vegyes port engedélyezése diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 6bbf3f72..fd0e89e1 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1347,6 +1347,15 @@ Sniffing type + + Enable Mixed Port Auth + + + Mixed Port Auth Pass + + + Mixed Port Auth User + Enable second mixed port diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index c777c4d9..a46609c1 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1338,6 +1338,15 @@ Тип сниффинга + + Включить авторизацию смешанного порта + + + Пароль авторизации смешанного порта + + + Пользователь авторизации смешанного порта + Включить второй смешанный порт diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index b26059ad..d40fccfd 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1344,6 +1344,15 @@ 流量探测类型 + + 启用混合端口认证 + + + 混合端口认证密码 + + + 混合端口认证用户名 + 开启第二个本地监听端口 diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index bacf2f11..2b9b9458 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1335,6 +1335,15 @@ 流量探測類型 + + 啟用混合連接埠驗證 + + + 混合連接埠驗證密碼 + + + 混合連接埠驗證使用者 + 開啟第二個本機監聽埠 diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs index 3a27c7da..fe72db31 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs @@ -22,6 +22,21 @@ public partial class CoreConfigSingboxService _coreConfig.inbounds.Add(inbound); inbound.listen_port = listenPort; + if (_config.Inbound.First().MixedPortAuthEnabled) + { + inbound.users = new() + { + new() + { + username = _config.Inbound.First().MixedPortAuthUser, + password = _config.Inbound.First().MixedPortAuthPass + } + }; + } + else + { + inbound.users = null; + } if (_config.Inbound.First().SecondLocalPortEnabled) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs index 7ae12c7c..8bb7d0a6 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs @@ -65,6 +65,23 @@ public partial class CoreConfigV2rayService inbound.sniffing.enabled = inItem.SniffingEnabled; inbound.sniffing.destOverride = inItem.DestOverride; inbound.sniffing.routeOnly = inItem.RouteOnly; + if (inItem.MixedPortAuthEnabled) + { + inbound.settings.auth = "password"; + inbound.settings.accounts = new List + { + new() + { + user = inItem.MixedPortAuthUser, + pass = inItem.MixedPortAuthPass + } + }; + } + else + { + inbound.settings.auth = "noauth"; + inbound.settings.accounts = null; + } return inbound; } diff --git a/v2rayN/ServiceLib/Services/DownloadService.cs b/v2rayN/ServiceLib/Services/DownloadService.cs index 77d3a7c1..a6ee41d2 100644 --- a/v2rayN/ServiceLib/Services/DownloadService.cs +++ b/v2rayN/ServiceLib/Services/DownloadService.cs @@ -212,7 +212,24 @@ public class DownloadService return null; } - return new WebProxy($"socks5://{Global.Loopback}:{port}"); + var webProxy = new WebProxy($"socks5://{Global.Loopback}:{port}"); + ApplyMixedPortAuth(webProxy); + return webProxy; + } + + private void ApplyMixedPortAuth(WebProxy webProxy) + { + var inbound = AppManager.Instance.Config?.Inbound?.FirstOrDefault(); + if (inbound?.MixedPortAuthEnabled != true) + { + return; + } + if (inbound.MixedPortAuthUser.IsNullOrEmpty() || inbound.MixedPortAuthPass.IsNullOrEmpty()) + { + return; + } + + webProxy.Credentials = new NetworkCredential(inbound.MixedPortAuthUser, inbound.MixedPortAuthPass); } private async Task SocketCheck(string ip, int port) diff --git a/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayN/ServiceLib/Services/SpeedtestService.cs index 362f64d0..b54ce0ea 100644 --- a/v2rayN/ServiceLib/Services/SpeedtestService.cs +++ b/v2rayN/ServiceLib/Services/SpeedtestService.cs @@ -305,6 +305,7 @@ public class SpeedtestService(Config config, Func updateF private async Task DoRealPing(ServerTestItem it) { var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}"); + ApplyMixedPortAuth(webProxy); var responseTime = await ConnectionHandler.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime); @@ -317,6 +318,7 @@ public class SpeedtestService(Config config, Func updateF await UpdateFunc(it.IndexId, "", ResUI.Speedtesting); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}"); + ApplyMixedPortAuth(webProxy); var url = _config.SpeedTestItem.SpeedTestUrl; var timeout = _config.SpeedTestItem.SpeedTestTimeout; await downloadHandle.DownloadDataAsync(url, webProxy, timeout, async (success, msg) => @@ -378,6 +380,22 @@ public class SpeedtestService(Config config, Func updateF return lstTest; } + private void ApplyMixedPortAuth(WebProxy webProxy) + { + var inbound = _config?.Inbound?.FirstOrDefault(); + if (inbound?.MixedPortAuthEnabled != true) + { + return; + } + if (inbound.MixedPortAuthUser.IsNullOrEmpty() || inbound.MixedPortAuthPass.IsNullOrEmpty()) + { + return; + } + + // Keep credentials out of URLs/logs and pass them via proxy credentials only. + webProxy.Credentials = new NetworkCredential(inbound.MixedPortAuthUser, inbound.MixedPortAuthPass); + } + private async Task UpdateFunc(string indexId, string delay, string speed = "") { await _updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed }); diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 1d26f02f..c1d7588f 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -14,6 +14,9 @@ public class OptionSettingViewModel : MyReactiveObject [Reactive] public bool newPort4LAN { get; set; } [Reactive] public string user { get; set; } [Reactive] public string pass { get; set; } + [Reactive] public bool mixedPortAuthEnabled { get; set; } + [Reactive] public string mixedPortAuthUser { get; set; } + [Reactive] public string mixedPortAuthPass { get; set; } [Reactive] public bool muxEnabled { get; set; } [Reactive] public bool logEnabled { get; set; } [Reactive] public string loglevel { get; set; } @@ -149,6 +152,9 @@ public class OptionSettingViewModel : MyReactiveObject newPort4LAN = inbound.NewPort4LAN; user = inbound.User; pass = inbound.Pass; + mixedPortAuthEnabled = inbound.MixedPortAuthEnabled; + mixedPortAuthUser = inbound.MixedPortAuthUser; + mixedPortAuthPass = inbound.MixedPortAuthPass; muxEnabled = _config.CoreBasicItem.MuxEnabled; logEnabled = _config.CoreBasicItem.LogEnabled; loglevel = _config.CoreBasicItem.Loglevel; @@ -334,6 +340,9 @@ public class OptionSettingViewModel : MyReactiveObject _config.Inbound.First().NewPort4LAN = newPort4LAN; _config.Inbound.First().User = user; _config.Inbound.First().Pass = pass; + _config.Inbound.First().MixedPortAuthEnabled = mixedPortAuthEnabled; + _config.Inbound.First().MixedPortAuthUser = mixedPortAuthUser; + _config.Inbound.First().MixedPortAuthPass = mixedPortAuthPass; if (_config.Inbound.Count > 1) { _config.Inbound.RemoveAt(1); diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 640a4f6f..6ccce9e5 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -59,6 +59,47 @@ VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbSettingsSocksPortTip}" TextWrapping="Wrap" /> + + + + + + + + this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.user, v => v.txtuser.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.pass, v => v.txtpass.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthEnabled, v => v.togMixedPortAuthEnabled.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthEnabled, v => v.txtMixedPortAuthUser.IsEnabled).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthEnabled, v => v.txtMixedPortAuthPass.IsEnabled).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthUser, v => v.txtMixedPortAuthUser.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthPass, v => v.txtMixedPortAuthPass.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.muxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.logEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.loglevel, v => v.cmbloglevel.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 5a545c65..6595409f 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -94,6 +94,52 @@ Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.TbSettingsSocksPortTip}" TextWrapping="Wrap" /> + + + + + + + + vm.newPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.user, v => v.txtuser.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.pass, v => v.txtpass.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthEnabled, v => v.togMixedPortAuthEnabled.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthEnabled, v => v.txtMixedPortAuthUser.IsEnabled).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthEnabled, v => v.txtMixedPortAuthPass.IsEnabled).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthUser, v => v.txtMixedPortAuthUser.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.mixedPortAuthPass, v => v.txtMixedPortAuthPass.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.muxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.logEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.loglevel, v => v.cmbloglevel.Text).DisposeWith(disposables);