From e3ea32979500cdebec49206872461eb5d996ae5a Mon Sep 17 00:00:00 2001 From: NagisaEfi <141222574+NagisaEfi@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:49:41 +0800 Subject: [PATCH 1/4] Update ResUI.zh-Hant.resx (#6049) --- v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 96 +++++++++++------------ 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index d9c8d58d..597a5b76 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -301,7 +301,7 @@ 掃描匯入分享链接成功 - 目前服務的真連接延遲: {0} ms + 目前服務的真連線延遲: {0} ms 操作成功 @@ -511,7 +511,7 @@ 清除所有服務統計資料 - 測試伺服器真連接延遲(多選) (Ctrl+R) + 測試伺服器真連線延遲(多選) (Ctrl+R) 按測試結果排序 @@ -683,7 +683,7 @@ 進階代理設定, 協定選擇(可選) - 允許來自區域網路的連接 + 允許來自區域網路的連線 啟動後隱藏視窗 @@ -1106,7 +1106,7 @@ 自动滚动到末尾 - 真連接測試地址 + 真連線測試位址 更新訂閱時只判斷別名是否存在 @@ -1187,7 +1187,7 @@ WebDav 密碼 - WebDav 伺服器地址 + WebDav 伺服器位址 遠端資料夾名稱(可選) @@ -1205,25 +1205,25 @@ 儲存介面佈局 - Geo文件來源(可選) + Geo檔案來源(可選) - 升级工具App不存在 + 升級工具App不存在 - sing-box ruleset文件來源(可選) + sing-box ruleset檔案來源(可選) - 路由规则集来源(可选) + 路由規則集來源(可選) - 中国区域用户可忽略此项 + 中國區域用戶可忽略此項 - 区域预置设置 + 區域預置設定 - 默认区域 + 預設區域 俄羅斯 @@ -1232,13 +1232,13 @@ 掃描圖片中的二維碼 - 地址(Url)無效 + 位址(Url)無效 請不要使用不安全的HTTP協定訂閱位址 - 安裝字體到系統中,重新啟動設定 + 安裝字型到系統中,重新啟動設定 是否確定退出? @@ -1247,22 +1247,22 @@ 備註備忘 - 拥塞控制算法 + 擁塞控制算法 - 设为活动节点 (Enter) + 設為活動節點 (Enter) - 刷新 + 重新整理 排序 - 延迟测试 + 延遲測試 - 规则 + 規則 随原配置 @@ -1271,79 +1271,79 @@ 全局 - 直连 + 直連 - 规则模式 + 規則模式 - 当前代理 + 目前代理 - 关闭所有连接 + 關閉所有連線 - Outbound默认解析策略 + Outbound預設解析策略 - 关闭连接 + 關閉連線 - 上传流量 + 上傳流量 - 上传速度 + 上傳速度 - 类型 + 類型 - 时间 + 時間 - 网络 + 網路 - 名称 + 名稱 - 主机 + 主機 - 下载流量 + 下載流量 - 下载速度 + 下載速度 - 延迟 + 延遲 - 默认 + 預設 - 当前连接 + 目前連線 - 多服务器最低延迟 (多选) + 多伺服器最低延遲 (多選) - 当前部分节点延迟测试 + 目前部分節點延遲測試 - 多服务器负载均衡 (多选) + 多伺服器負載平衡 (多選) - 添加[Hysteria2]服务器 + 添加[Hysteria2]伺服器 - Hysteria 最大带宽(Up/Dw) + Hysteria 最大頻寬(Up/Dw) - 主界面布局方向(需重启) + 主界面佈局方向(需重啟) - 添加[WireGuard]服务器 + 添加[WireGuard]伺服器 PrivateKey @@ -1355,15 +1355,15 @@ Address(Ip,Ipv6) - 使用系统hosts + 使用系統hosts - 路由链 + 路由鏈 - Outbound域名解析地址 + Outbound域名解析位址 - 混淆密码(obfs password) + 混淆密碼(obfs password) \ No newline at end of file From f921ff7d7746c68c24d85897e29c88185a693d55 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:00:01 +0800 Subject: [PATCH 2/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b618be4..1724e118 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # v2rayN -A GUI client for Windows, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores) +A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master) From 9d638968a91396eebf4020880575ff7e35a6e642 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:22:56 +0800 Subject: [PATCH 3/4] Use tun mode without running with sudo in linux --- v2rayN/ServiceLib/Handler/CoreHandler.cs | 47 ++++++++++++++----- v2rayN/ServiceLib/Models/ConfigItems.cs | 1 + v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 36 ++++++++++++++ v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 12 +++++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 12 +++++ v2rayN/ServiceLib/ServiceLib.csproj | 2 +- .../ViewModels/OptionSettingViewModel.cs | 3 ++ .../ViewModels/StatusBarViewModel.cs | 26 ++++++++-- .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 6 +++ .../Views/OptionSettingWindow.axaml | 25 +++++++++- .../Views/OptionSettingWindow.axaml.cs | 1 + .../Views/StatusBarView.axaml.cs | 2 +- 15 files changed, 190 insertions(+), 19 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 77d91597..94cad4a5 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -68,6 +68,7 @@ namespace ServiceLib.Handler { ShowMsg(true, $"{node.GetSummary()}"); await CoreStop(); + await Task.Delay(100); await CoreStart(node); //In tun mode, do a delay check and restart the core @@ -169,21 +170,12 @@ namespace ServiceLib.Handler ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}"); ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); - //ECoreType coreType; - //if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun) - //{ - // coreType = ECoreType.sing_box; - //} - //else - //{ - // coreType = LazyConfig.Instance.GetCoreType(node, node.configType); - //} var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); _config.RunningCoreType = coreType; var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; - var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog); + var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true); if (proc is null) { return; @@ -225,7 +217,7 @@ namespace ServiceLib.Handler if (result.Success) { var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); - var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true); + var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true, true); if (proc2 is not null) { _processPre = proc2; @@ -243,7 +235,7 @@ namespace ServiceLib.Handler try { var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); - var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true); + var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false); if (proc is null) { return -1; @@ -264,17 +256,28 @@ namespace ServiceLib.Handler _updateFunc?.Invoke(notify, msg); } + private bool IsNeedSudo(ECoreType eCoreType) + { + return _config.TunModeItem.EnableTun + && eCoreType == ECoreType.sing_box + && Utils.IsLinux() + && _config.TunModeItem.LinuxSudoPassword.IsNotEmpty() + ; + } + #endregion Private #region Process - private async Task RunProcess(CoreInfo coreInfo, string configPath, bool displayLog) + private async Task RunProcess(CoreInfo coreInfo, string configPath, bool displayLog, bool mayNeedSudo) { var fileName = CoreFindExe(coreInfo); if (Utils.IsNullOrEmpty(fileName)) { return null; } + + var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType); try { Process proc = new() @@ -292,6 +295,15 @@ namespace ServiceLib.Handler StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, } }; + + if (isNeedSudo) + { + proc.StartInfo.FileName = $"/bin/sudo"; + proc.StartInfo.Arguments = $"-S {fileName} {string.Format(coreInfo.Arguments, configPath)}"; + proc.StartInfo.StandardInputEncoding = Encoding.UTF8; + proc.StartInfo.RedirectStandardInput = true; + } + var startUpErrorMessage = new StringBuilder(); var startUpSuccessful = false; if (displayLog) @@ -313,6 +325,15 @@ namespace ServiceLib.Handler }; } proc.Start(); + + if (isNeedSudo) + { + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword); + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword); + } + if (displayLog) { proc.BeginOutputReadLine(); diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 84c9ff3f..8ec47b88 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -161,6 +161,7 @@ public int Mtu { get; set; } public bool EnableExInbound { get; set; } public bool EnableIPv6Address { get; set; } + public string? LinuxSudoPassword { get; set; } } [Serializable] diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 26864bef..ccb2b939 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3148,6 +3148,42 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Linux system sudo password 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPassword { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPassword", resourceCulture); + } + } + + /// + /// 查找类似 Please set the sudo password in Tun mode settings first 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPasswordIsEmpty { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPasswordIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 Please do not run this app with sudo 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPasswordNotSudoRunApp { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPasswordNotSudoRunApp", resourceCulture); + } + } + + /// + /// 查找类似 The password will only be stored in the local file. 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPasswordTip { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPasswordTip", resourceCulture); + } + } + /// /// 查找类似 Enable Log 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 41753419..099887ef 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1369,4 +1369,16 @@ (Domain or IP or ProcName) and Port and Protocol and InboundTag => OutboundTag + + Linux system sudo password + + + The password will only be stored in the local file. + + + Please set the sudo password in Tun mode settings first + + + Please do not run this app with sudo + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 0ef7eaef..c5238179 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1369,4 +1369,16 @@ Remarks Memo + + Linux system sudo password + + + The password will only be stored in the local file. + + + Please set the sudo password in Tun mode settings first + + + Please do not run this app with sudo + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index bd2d6cd6..255b578e 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1369,4 +1369,16 @@ Remote (WebDAV) + + Linux system sudo password + + + The password will only be stored in the local file. + + + Please set the sudo password in Tun mode settings first + + + Please do not run this app with sudo + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 7494aa4d..07f9b463 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1366,4 +1366,16 @@ 备注备忘 + + Linux系统的sudo密码 + + + 密码只会存储在本地文件中,没有密码无法开启Tun + + + 请先在Tun模式设置中设置sudo密码 + + + 请不要用sudo运行本app + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 597a5b76..1a172b35 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1366,4 +1366,16 @@ 混淆密碼(obfs password) + + Linux系統的sudo密碼 + + + 密碼只會儲存在本機檔案中,沒有密碼無法開啟Tun + + + 請先在Tun模式設定中設定sudo密碼 + + + 請不要用sudo來運行本app + \ No newline at end of file diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj index 0511465a..18aeed52 100644 --- a/v2rayN/ServiceLib/ServiceLib.csproj +++ b/v2rayN/ServiceLib/ServiceLib.csproj @@ -14,7 +14,7 @@ - + diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 65369944..40aadaca 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -85,6 +85,7 @@ namespace ServiceLib.ViewModels [Reactive] public int TunMtu { get; set; } [Reactive] public bool TunEnableExInbound { get; set; } [Reactive] public bool TunEnableIPv6Address { get; set; } + [Reactive] public string TunLinuxSudoPassword { get; set; } #endregion Tun mode @@ -197,6 +198,7 @@ namespace ServiceLib.ViewModels TunMtu = _config.TunModeItem.Mtu; TunEnableExInbound = _config.TunModeItem.EnableExInbound; TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address; + TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPassword; #endregion Tun mode @@ -340,6 +342,7 @@ namespace ServiceLib.ViewModels _config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; + _config.TunModeItem.LinuxSudoPassword = TunLinuxSudoPassword; //coreType await SaveCoreType(); diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 70de9685..47c7312a 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -108,7 +108,7 @@ namespace ServiceLib.ViewModels SelectedServer = new(); RunningServerToolTipText = "-"; - if (_config.TunModeItem.EnableTun && AppHandler.Instance.IsAdministrator) + if (_config.TunModeItem.EnableTun && AllowEnableTun()) { EnableTun = true; } @@ -414,10 +414,17 @@ namespace ServiceLib.ViewModels { _config.TunModeItem.EnableTun = EnableTun; // When running as a non-administrator, reboot to administrator mode - if (EnableTun && !AppHandler.Instance.IsAdministrator) + if (EnableTun && AllowEnableTun() == false) { _config.TunModeItem.EnableTun = false; - Locator.Current.GetService()?.RebootAsAdmin(); + if (Utils.IsWindows()) + { + Locator.Current.GetService()?.RebootAsAdmin(); + } + else if (Utils.IsLinux()) + { + NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty); + } return; } await ConfigHandler.SaveConfig(_config); @@ -425,6 +432,19 @@ namespace ServiceLib.ViewModels } } + private bool AllowEnableTun() + { + if (Utils.IsWindows()) + { + return AppHandler.Instance.IsAdministrator; + } + else if (Utils.IsLinux()) + { + return _config.TunModeItem.LinuxSudoPassword.IsNotEmpty(); + } + return false; + } + #endregion System proxy and Routings #region UI diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 4d4e67f7..377e4689 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -117,6 +117,11 @@ namespace v2rayN.Desktop.Views } else { + if (AppHandler.Instance.IsAdministrator) + { + this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}"; + NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp); + } menuRebootAsAdmin.IsVisible = false; menuSettingsSetUWP.IsVisible = false; menuGlobalHotkeySetting.IsVisible = false; @@ -282,6 +287,7 @@ namespace v2rayN.Desktop.Views e.Cancel = true; ShowHideWindow(false); break; + case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown: await ViewModel?.MyAppExitAsync(true); break; diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index e56e6c56..6176dfe3 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -489,7 +489,7 @@ HorizontalAlignment="Left" Classes="Margin8" /> - + + @@ -794,6 +795,28 @@ Grid.Column="1" HorizontalAlignment="Left" Classes="Margin8" /> + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index 2a024fd7..22b96ce8 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -152,6 +152,7 @@ namespace v2rayN.Desktop.Views 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.TunLinuxSudoPassword, v => v.txtLinuxSudoPassword.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs index db574dc3..63587ef9 100644 --- a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs @@ -41,7 +41,7 @@ namespace v2rayN.Desktop.Views this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables); }); - spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator); + //spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator); } private async Task UpdateViewHandler(EViewAction action, object? obj) From 28019dc511c0601ca638097b53a8dc5a5225465e Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:45:23 +0800 Subject: [PATCH 4/4] Migrate xray traffic statistics to Metrics https://xtls.github.io/config/metrics.html --- .../ServiceLib/Handler/StatisticsHandler.cs | 9 +- v2rayN/ServiceLib/Models/V2rayConfig.cs | 352 ++---------------- v2rayN/ServiceLib/Models/V2rayMetricsVars.cs | 20 + .../CoreConfig/CoreConfigV2rayService.cs | 11 +- .../Statistics/StatisticsXrayService.cs | 106 ++++++ 5 files changed, 158 insertions(+), 340 deletions(-) create mode 100644 v2rayN/ServiceLib/Models/V2rayMetricsVars.cs create mode 100644 v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs diff --git a/v2rayN/ServiceLib/Handler/StatisticsHandler.cs b/v2rayN/ServiceLib/Handler/StatisticsHandler.cs index 97d4f7b3..377638cb 100644 --- a/v2rayN/ServiceLib/Handler/StatisticsHandler.cs +++ b/v2rayN/ServiceLib/Handler/StatisticsHandler.cs @@ -9,7 +9,8 @@ private ServerStatItem? _serverStatItem; private List _lstServerStat; private Action? _updateFunc; - private StatisticsV2rayService? _statisticsV2Ray; + //private StatisticsV2rayService? _statisticsV2Ray; + private StatisticsXrayService? _statisticsXray; private StatisticsSingboxService? _statisticsSingbox; public List ServerStat => _lstServerStat; @@ -25,7 +26,8 @@ await InitData(); - _statisticsV2Ray = new StatisticsV2rayService(config, UpdateServerStatHandler); + //_statisticsV2Ray = new StatisticsV2rayService(config, UpdateServerStatHandler); + _statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler); _statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler); } @@ -33,7 +35,8 @@ { try { - _statisticsV2Ray?.Close(); + //_statisticsV2Ray?.Close(); + _statisticsXray?.Close(); _statisticsSingbox?.Close(); } catch (Exception ex) diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index 593e3710..b5623398 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -2,60 +2,33 @@ using System.Text.Json.Serialization; namespace ServiceLib.Models { - /// - /// v2ray配置文件实体类 例子SampleConfig.txt - /// public class V2rayConfig { - /// - /// Properties that do not belong to Ray - /// public string? remarks { get; set; } - /// - /// 日志配置 - /// public Log4Ray log { get; set; } - /// - /// 传入连接配置 - /// public List inbounds { get; set; } - /// - /// 传出连接配置 - /// public List outbounds { get; set; } - /// - /// 统计需要, 空对象 - /// - public Stats4Ray stats { get; set; } + public Stats4Ray? stats { get; set; } - /// - public API4Ray api { get; set; } + public Metrics4Ray? metrics { get; set; } - /// - public Policy4Ray policy { get; set; } + public Policy4Ray? policy { get; set; } - /// - /// DNS 配置 - /// public object dns { get; set; } - /// - /// 路由配置 - /// public Routing4Ray routing { get; set; } } public class Stats4Ray { } - public class API4Ray + public class Metrics4Ray { public string tag { get; set; } - public List services { get; set; } } public class Policy4Ray @@ -71,124 +44,59 @@ namespace ServiceLib.Models public class Log4Ray { - /// - /// - /// - public string access { get; set; } + public string? access { get; set; } - /// - /// - /// - public string error { get; set; } + public string? error { get; set; } - /// - /// - /// - public string loglevel { get; set; } + public string? loglevel { get; set; } } public class Inbounds4Ray { public string tag { get; set; } - /// - /// - /// public int port { get; set; } - /// - /// - /// public string listen { get; set; } - /// - /// - /// public string protocol { get; set; } - /// - /// - /// public Sniffing4Ray sniffing { get; set; } - /// - /// - /// public Inboundsettings4Ray settings { get; set; } - - /// - /// - /// - public StreamSettings4Ray streamSettings { get; set; } } public class Inboundsettings4Ray { - /// - /// - /// - public string auth { get; set; } + public string? auth { get; set; } - /// - /// - /// - public bool udp { get; set; } + public bool? udp { get; set; } - /// - /// - /// - public string ip { get; set; } + public string? ip { get; set; } - /// - /// api 使用 - /// - public string address { get; set; } + public string? address { get; set; } - /// - /// - /// - public List clients { get; set; } + public List? clients { get; set; } - /// - /// VLESS - /// - public string decryption { get; set; } + public string? decryption { get; set; } - public bool allowTransparent { get; set; } + public bool? allowTransparent { get; set; } - public List accounts { get; set; } + public List? accounts { get; set; } } public class UsersItem4Ray { - /// - /// - /// - public string id { get; set; } + public string? id { get; set; } - /// - /// - /// - public int alterId { get; set; } + public int? alterId { get; set; } - /// - /// - /// - public string email { get; set; } + public string? email { get; set; } - /// - /// - /// - public string security { get; set; } + public string? security { get; set; } - /// - /// VLESS - /// - public string encryption { get; set; } + public string? encryption { get; set; } - /// - /// VLESS - /// public string? flow { get; set; } } @@ -201,57 +109,27 @@ namespace ServiceLib.Models public class Outbounds4Ray { - /// - /// 默认值agentout - /// public string tag { get; set; } - /// - /// - /// public string protocol { get; set; } - /// - /// - /// public Outboundsettings4Ray settings { get; set; } - /// - /// - /// public StreamSettings4Ray streamSettings { get; set; } - /// - /// - /// public Mux4Ray mux { get; set; } } public class Outboundsettings4Ray { - /// - /// - /// public List? vnext { get; set; } - /// - /// - /// - public List servers { get; set; } + public List? servers { get; set; } - /// - /// - /// - public Response4Ray response { get; set; } + public Response4Ray? response { get; set; } - /// - /// - /// public string domainStrategy { get; set; } - /// - /// - /// public int? userLevel { get; set; } public FragmentItem4Ray? fragment { get; set; } @@ -259,85 +137,40 @@ namespace ServiceLib.Models public class VnextItem4Ray { - /// - /// - /// public string address { get; set; } - /// - /// - /// public int port { get; set; } - /// - /// - /// public List users { get; set; } } public class ServersItem4Ray { - /// - /// - /// public string email { get; set; } - /// - /// - /// public string address { get; set; } - /// - /// - /// public string? method { get; set; } - /// - /// - /// public bool? ota { get; set; } - /// - /// - /// public string? password { get; set; } - /// - /// - /// public int port { get; set; } - /// - /// - /// public int? level { get; set; } - /// - /// trojan - /// public string flow { get; set; } - /// - /// - /// public List users { get; set; } } public class SocksUsersItem4Ray { - /// - /// - /// public string user { get; set; } - /// - /// - /// public string pass { get; set; } - /// - /// - /// public int? level { get; set; } } @@ -351,17 +184,11 @@ namespace ServiceLib.Models public class Response4Ray { - /// - /// - /// public string type { get; set; } } public class Dns4Ray { - /// - /// - /// public List servers { get; set; } } @@ -373,19 +200,10 @@ namespace ServiceLib.Models public class Routing4Ray { - /// - /// - /// public string domainStrategy { get; set; } - /// - /// - /// public string? domainMatcher { get; set; } - /// - /// - /// public List rules { get; set; } public List? balancers { get; set; } @@ -426,87 +244,39 @@ namespace ServiceLib.Models public class StreamSettings4Ray { - /// - /// - /// public string network { get; set; } - /// - /// - /// public string security { get; set; } - /// - /// - /// public TlsSettings4Ray? tlsSettings { get; set; } - /// - /// Tcp传输额外设置 - /// public TcpSettings4Ray? tcpSettings { get; set; } - /// - /// Kcp传输额外设置 - /// public KcpSettings4Ray? kcpSettings { get; set; } - /// - /// ws传输额外设置 - /// public WsSettings4Ray? wsSettings { get; set; } - /// - /// - /// public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; } - /// - /// - /// public SplithttpSettings4Ray? splithttpSettings { get; set; } - /// - /// h2传输额外设置 - /// public HttpSettings4Ray? httpSettings { get; set; } - /// - /// QUIC - /// public QuicSettings4Ray? quicSettings { get; set; } - /// - /// VLESS only - /// public TlsSettings4Ray? realitySettings { get; set; } - /// - /// grpc - /// public GrpcSettings4Ray? grpcSettings { get; set; } - /// - /// sockopt - /// public Sockopt4Ray? sockopt { get; set; } } public class TlsSettings4Ray { - /// - /// 是否允许不安全连接(用于客户端) - /// public bool? allowInsecure { get; set; } - /// - /// - /// public string? serverName { get; set; } - /// - /// - /// public List? alpn { get; set; } public string? fingerprint { get; set; } @@ -519,115 +289,58 @@ namespace ServiceLib.Models public class TcpSettings4Ray { - /// - /// 数据包头部伪装设置 - /// public Header4Ray header { get; set; } } public class Header4Ray { - /// - /// 伪装 - /// public string type { get; set; } - /// - /// 结构复杂,直接存起来 - /// public object request { get; set; } - /// - /// 结构复杂,直接存起来 - /// public object response { get; set; } } public class KcpSettings4Ray { - /// - /// - /// public int mtu { get; set; } - /// - /// - /// public int tti { get; set; } - /// - /// - /// public int uplinkCapacity { get; set; } - /// - /// - /// public int downlinkCapacity { get; set; } - /// - /// - /// public bool congestion { get; set; } - /// - /// - /// public int readBufferSize { get; set; } - /// - /// - /// public int writeBufferSize { get; set; } - /// - /// - /// public Header4Ray header { get; set; } - /// - /// - /// public string seed { get; set; } } public class WsSettings4Ray { - /// - /// - /// public string path { get; set; } - /// - /// - /// public Headers4Ray headers { get; set; } } public class Headers4Ray { - /// - /// - /// public string Host { get; set; } - /// - /// 用户代理 - /// [JsonPropertyName("User-Agent")] public string UserAgent { get; set; } } public class HttpupgradeSettings4Ray { - /// - /// - /// public string? path { get; set; } - /// - /// - /// public string? host { get; set; } } @@ -644,32 +357,17 @@ namespace ServiceLib.Models public class HttpSettings4Ray { - /// - /// - /// public string? path { get; set; } - /// - /// - /// public List? host { get; set; } } public class QuicSettings4Ray { - /// - /// - /// public string security { get; set; } - /// - /// - /// public string key { get; set; } - /// - /// - /// public Header4Ray header { get; set; } } @@ -686,14 +384,8 @@ namespace ServiceLib.Models public class AccountsItem4Ray { - /// - /// - /// public string user { get; set; } - /// - /// - /// public string pass { get; set; } } diff --git a/v2rayN/ServiceLib/Models/V2rayMetricsVars.cs b/v2rayN/ServiceLib/Models/V2rayMetricsVars.cs new file mode 100644 index 00000000..acd4ea45 --- /dev/null +++ b/v2rayN/ServiceLib/Models/V2rayMetricsVars.cs @@ -0,0 +1,20 @@ +using System.Collections; + +namespace ServiceLib.Models +{ + internal class V2rayMetricsVars + { + public V2rayMetricsVarsStats? stats { get; set; } + } +} + +public class V2rayMetricsVarsStats +{ + public Hashtable? outbound { get; set; } +} + +public class V2rayMetricsVarsLink +{ + public long downlink { get; set; } + public long uplink { get; set; } +} \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 99acf423..ced600cf 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -366,8 +366,8 @@ namespace ServiceLib.Services.CoreConfig else { v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel; - v2rayConfig.log.access = ""; - v2rayConfig.log.error = ""; + v2rayConfig.log.access = null; + v2rayConfig.log.error = null; } } catch (Exception ex) @@ -1120,17 +1120,14 @@ namespace ServiceLib.Services.CoreConfig if (_config.GuiItem.EnableStatistics) { string tag = EInboundProtocol.api.ToString(); - API4Ray apiObj = new(); + Metrics4Ray apiObj = new(); Policy4Ray policyObj = new(); SystemPolicy4Ray policySystemSetting = new(); - string[] services = { "StatsService" }; - v2rayConfig.stats = new Stats4Ray(); apiObj.tag = tag; - apiObj.services = services.ToList(); - v2rayConfig.api = apiObj; + v2rayConfig.metrics = apiObj; policySystemSetting.statsOutboundDownlink = true; policySystemSetting.statsOutboundUplink = true; diff --git a/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs b/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs new file mode 100644 index 00000000..72a51905 --- /dev/null +++ b/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs @@ -0,0 +1,106 @@ +namespace ServiceLib.Services.Statistics +{ + public class StatisticsXrayService + { + private const long linkBase = 1024; + private string _url; + private ServerSpeedItem _serverSpeedItem = new(); + private Config _config; + private bool _exitFlag; + private Action? _updateFunc; + + public StatisticsXrayService(Config config, Action updateFunc) + { + _config = config; + _updateFunc = updateFunc; + _exitFlag = false; + _url = $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars"; + + Task.Run(Run); + } + + public void Close() + { + _exitFlag = true; + } + + private async void Run() + { + while (!_exitFlag) + { + await Task.Delay(1000); + try + { + if (!_config.IsRunningCore(ECoreType.Xray)) + { + continue; + } + + var result = await HttpClientHelper.Instance.TryGetAsync(_url); + if (result != null) + { + var server = ParseOutput(result) ?? new ServerSpeedItem(); + _updateFunc?.Invoke(server); + } + } + catch + { + // ignored + } + } + } + + private ServerSpeedItem? ParseOutput(string result) + { + try + { + var source = JsonUtils.Deserialize(result); + if (source?.stats?.outbound == null) + { + return null; + } + + ServerSpeedItem server = new(); + foreach (string key in source.stats.outbound.Keys) + { + var value = source.stats.outbound[key]; + if (value == null) continue; + var state = JsonUtils.Deserialize(value.ToString()); + + if (key.StartsWith(Global.ProxyTag)) + { + server.ProxyUp += state.uplink / linkBase; + server.ProxyDown += state.downlink / linkBase; + } + else if (key == Global.DirectTag) + { + server.DirectUp = state.uplink / linkBase; + server.DirectDown = state.downlink / linkBase; + } + } + + if (server.DirectDown < _serverSpeedItem.DirectDown || server.ProxyDown < _serverSpeedItem.ProxyDown) + { + _serverSpeedItem = new(); + return null; + } + + ServerSpeedItem curItem = new() + { + ProxyUp = server.ProxyUp - _serverSpeedItem.ProxyUp, + ProxyDown = server.ProxyDown - _serverSpeedItem.ProxyDown, + DirectUp = server.DirectUp - _serverSpeedItem.DirectUp, + DirectDown = server.DirectDown - _serverSpeedItem.DirectDown, + }; + _serverSpeedItem = server; + return curItem; + } + catch + { + // ignored + } + + return null; + } + } +} \ No newline at end of file