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)
[](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