Compare commits

..

12 Commits

Author SHA1 Message Date
2dust
1d1f5641eb up 7.13.5 2025-07-31 20:43:00 +08:00
DHR60
3f79df21d9 Fix missing hysteria2 arguments (#7673) 2025-07-31 16:56:39 +08:00
2dust
ac1231ad54 sing-box LAN listening address changed to 0.0.0.0
https://github.com/2dust/v2rayN/discussions/7669
2025-07-30 21:08:37 +08:00
2dust
8662d94ab6 Fixed bug for macos kill_as_sudo 2025-07-30 20:33:51 +08:00
2dust
3d23f3e3a2 Optimized and improved the code
Optimized and improved the code for killing core processes in non-Windows environments. Now uses a shell script for precise processing.
2025-07-30 19:52:45 +08:00
2dust
6715d7dce6 up 7.13.4 2025-07-29 20:44:43 +08:00
2dust
dad35f57d0 Fixed an issue where root processes could not be exited on macOS 2025-07-29 20:23:42 +08:00
2dust
f779e311ed Optimize code and remove unused resources 2025-07-29 19:42:59 +08:00
maximilionus
ce7c41e3ff Unix platform elevation enhancements v2 (#7658)
* Remove multiple send password actions on Unix elev

* Remove CoreAdminHandler password verification

This is useless since already handled in
v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs with
CheckSudoPasswordAsync().

* Disable caching and prompt for sudo call

* Cleanup CoreAdminHandler pwd verify remains

* Migrate sudo opts to initial pwd verification
2025-07-29 19:28:09 +08:00
DHR60
74bb01d044 Improves private IP address detection (#7657) 2025-07-29 19:18:13 +08:00
DHR60
82f9698c0d Supports IPv6 addresses in profile summary (#7656) 2025-07-29 19:16:50 +08:00
2dust
6911883995 Fixed the issue where process does not accept sudo password input stream for the first time 2025-07-27 14:53:27 +08:00
22 changed files with 222 additions and 100 deletions

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.13.3</Version>
<Version>7.13.5</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -223,4 +223,28 @@ public static class FileManager
// ignored
}
}
/// <summary>
/// Creates a Linux shell file with the specified contents.
/// </summary>
/// <param name="fileName"></param>
/// <param name="contents"></param>
/// <param name="overwrite"></param>
/// <returns></returns>
public static async Task<string> CreateLinuxShellFile(string fileName, string contents, bool overwrite)
{
var shFilePath = Utils.GetBinConfigPath(fileName);
// Check if the file already exists and if we should overwrite it
if (!overwrite && File.Exists(shFilePath))
{
return shFilePath;
}
File.Delete(shFilePath);
await File.WriteAllTextAsync(shFilePath, contents);
await Utils.SetLinuxChmod(shFilePath);
return shFilePath;
}
}

View File

@@ -390,13 +390,44 @@ public class Utils
{
if (IPAddress.TryParse(ip, out var address))
{
// Loopback address check (127.0.0.1 for IPv4, ::1 for IPv6)
if (IPAddress.IsLoopback(address))
return true;
var ipBytes = address.GetAddressBytes();
if (ipBytes[0] == 10)
return true;
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31)
return true;
if (ipBytes[0] == 192 && ipBytes[1] == 168)
return true;
if (address.AddressFamily == AddressFamily.InterNetwork)
{
// IPv4 private address check
if (ipBytes[0] == 10)
return true;
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31)
return true;
if (ipBytes[0] == 192 && ipBytes[1] == 168)
return true;
}
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
// IPv6 private address check
// Link-local address fe80::/10
if (ipBytes[0] == 0xfe && (ipBytes[1] & 0xc0) == 0x80)
return true;
// Unique local address fc00::/7 (typically fd00::/8)
if ((ipBytes[0] & 0xfe) == 0xfc)
return true;
// Private portion in IPv4-mapped addresses ::ffff:0:0/96
if (address.IsIPv4MappedToIPv6)
{
var ipv4Bytes = ipBytes.Skip(12).ToArray();
if (ipv4Bytes[0] == 10)
return true;
if (ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31)
return true;
if (ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168)
return true;
}
}
}
return false;

View File

@@ -38,6 +38,8 @@ public class Global
public const string PacFileName = NamespaceSample + "pac";
public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh";
public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh";
public const string KillAsSudoOSXShellFileName = NamespaceSample + "kill_as_sudo_osx_sh";
public const string KillAsSudoLinuxShellFileName = NamespaceSample + "kill_as_sudo_linux_sh";
public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp";

View File

@@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Text;
using CliWrap;
using CliWrap.Buffered;
namespace ServiceLib.Handler;
@@ -9,9 +10,9 @@ public class CoreAdminHandler
private static readonly Lazy<CoreAdminHandler> _instance = new(() => new());
public static CoreAdminHandler Instance => _instance.Value;
private Config _config;
private readonly string _sudoAccessText = "SUDO_ACCESS_VERIFIED";
private Action<bool, string>? _updateFunc;
private int _linuxSudoPid = -1;
private const string _tag = "CoreAdminHandler";
public async Task Init(Config config, Action<bool, string> updateFunc)
{
@@ -21,6 +22,8 @@ public class CoreAdminHandler
}
_config = config;
_updateFunc = updateFunc;
await Task.CompletedTask;
}
private void UpdateFunc(bool notify, string msg)
@@ -30,8 +33,11 @@ public class CoreAdminHandler
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
{
StringBuilder sb = new();
sb.AppendLine("#!/bin/bash");
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
sb.AppendLine($"sudo -S {cmdLine}");
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
Process proc = new()
{
@@ -45,26 +51,18 @@ public class CoreAdminHandler
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
}
};
var sudoVerified = false;
DataReceivedEventHandler dataHandler = (sender, e) =>
void dataHandler(object sender, DataReceivedEventArgs e)
{
if (e.Data.IsNotEmpty())
{
if (!sudoVerified && e.Data.Contains(_sudoAccessText))
{
sudoVerified = true;
UpdateFunc(false, ResUI.SudoPwdVerfiedSuccessTip + Environment.NewLine);
return;
}
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
}
proc.OutputDataReceived += dataHandler;
proc.ErrorDataReceived += dataHandler;
@@ -73,8 +71,6 @@ public class CoreAdminHandler
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
@@ -96,35 +92,27 @@ public class CoreAdminHandler
return;
}
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
try
{
var shellFileName = Utils.IsOSX() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName;
var shFilePath = await FileManager.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true);
if (shFilePath.Contains(' '))
{
shFilePath = shFilePath.AppendQuotes();
}
var arg = new List<string>() { "-c", $"sudo -S {shFilePath} {_linuxSudoPid}" };
var result = await Cli.Wrap(Global.LinuxBash)
.WithArguments(arg)
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
.ExecuteBufferedAsync();
await Cli.Wrap(shFilePath)
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
.ExecuteAsync();
UpdateFunc(false, result.StandardOutput.ToString());
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
_linuxSudoPid = -1;
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
var shFilePath = Utils.GetBinConfigPath(fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (Utils.IsAdministrator())
{
sb.AppendLine($"{cmdLine}");
}
else
{
sb.AppendLine($"sudo -S echo \"{_sudoAccessText}\" && sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
return shFilePath;
}
}

View File

@@ -280,13 +280,13 @@ public class CoreHandler
if (displayLog)
{
DataReceivedEventHandler dataHandler = (sender, e) =>
void dataHandler(object sender, DataReceivedEventArgs e)
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
}
proc.OutputDataReceived += dataHandler;
proc.ErrorDataReceived += dataHandler;
}

View File

@@ -130,7 +130,7 @@ public sealed class CoreInfoHandler
{
CoreType = ECoreType.hysteria,
CoreExes = ["hysteria"],
Arguments = "",
Arguments = "-c {0}",
Url = GetCoreUrl(ECoreType.hysteria),
},
@@ -180,7 +180,7 @@ public sealed class CoreInfoHandler
{
CoreType = ECoreType.hysteria2,
CoreExes = ["hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria"],
Arguments = "",
Arguments = "-c {0}",
Url = GetCoreUrl(ECoreType.hysteria2),
},

View File

@@ -18,14 +18,7 @@ public class ProxySettingLinux
private static async Task ExecCmd(List<string> args)
{
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{
var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName);
await File.AppendAllTextAsync(fileName, contents);
await Utils.SetLinuxChmod(fileName);
}
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false);
await Utils.GetCliWrapOutput(fileName, args);
}

View File

@@ -23,14 +23,7 @@ public class ProxySettingOSX
private static async Task ExecCmd(List<string> args)
{
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{
var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName);
await File.AppendAllTextAsync(fileName, contents);
await Utils.SetLinuxChmod(fileName);
}
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName), false);
await Utils.GetCliWrapOutput(fileName, args);
}

View File

@@ -32,7 +32,7 @@ public class ProfileItem : ReactiveObject
public string GetSummary()
{
var summary = $"[{(ConfigType).ToString()}] ";
var arrAddr = Address.Split('.');
var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.');
var addr = arrAddr.Length switch
{
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}",

View File

@@ -2211,15 +2211,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Sudo password has been verified successfully, please ignore the incorrect password prompts! 的本地化字符串。
/// </summary>
public static string SudoPwdVerfiedSuccessTip {
get {
return ResourceManager.GetString("SudoPwdVerfiedSuccessTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 Address 的本地化字符串。
/// </summary>

View File

@@ -1395,9 +1395,6 @@
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
</data>
<data name="SudoPwdVerfiedSuccessTip" xml:space="preserve">
<value>Sudo password has been verified successfully, please ignore the incorrect password prompts!</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>

View File

@@ -1395,9 +1395,6 @@
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
</data>
<data name="SudoPwdVerfiedSuccessTip" xml:space="preserve">
<value>Sudo password has been verified successfully, please ignore the incorrect password prompts!</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>

View File

@@ -1395,9 +1395,6 @@
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
</data>
<data name="SudoPwdVerfiedSuccessTip" xml:space="preserve">
<value>Sudo password has been verified successfully, please ignore the incorrect password prompts!</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>

View File

@@ -1395,9 +1395,6 @@
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
</data>
<data name="SudoPwdVerfiedSuccessTip" xml:space="preserve">
<value>Sudo password has been verified successfully, please ignore the incorrect password prompts!</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>

View File

@@ -1392,9 +1392,6 @@
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填写配置文件别名,请确保存在并唯一</value>
</data>
<data name="SudoPwdVerfiedSuccessTip" xml:space="preserve">
<value>sudo 密码已经验证成功,请忽略错误密码提示!</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密码错误,请重试。</value>
</data>

View File

@@ -1392,9 +1392,6 @@
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填寫設定檔別名,請確保存在並唯一</value>
</data>
<data name="SudoPwdVerfiedSuccessTip" xml:space="preserve">
<value>sudo 密碼已經驗證成功,請忽略錯誤密碼提示!</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密碼錯誤,請重試。</value>
</data>

View File

@@ -0,0 +1,61 @@
#!/bin/bash
#
# Process Terminator Script for Linux
# This script forcibly terminates a process and all its child processes
#
# Check if PID argument is provided
if [ $# -ne 1 ]; then
echo "Usage: $0 <PID>"
exit 1
fi
PID=$1
# Validate that input is a valid PID (numeric)
if ! [[ "$PID" =~ ^[0-9]+$ ]]; then
echo "Error: The PID must be a numeric value"
exit 1
fi
# Check if the process exists
if ! ps -p $PID > /dev/null; then
echo "Warning: No process found with PID $PID"
exit 0
fi
# Recursive function to find and kill all child processes
kill_children() {
local parent=$1
local children=$(ps -o pid --no-headers --ppid "$parent")
# Output information about processes being terminated
echo "Processing children of PID: $parent..."
# Process each child
for child in $children; do
# Recursively find and kill child's children first
kill_children "$child"
# Force kill the child process
echo "Terminating child process: $child"
kill -9 "$child" 2>/dev/null || true
done
}
echo "============================================"
echo "Starting termination of process $PID and all its children"
echo "============================================"
# Find and kill all child processes
kill_children "$PID"
# Finally kill the main process
echo "Terminating main process: $PID"
kill -9 "$PID" 2>/dev/null || true
echo "============================================"
echo "Process $PID and all its children have been terminated"
echo "============================================"
exit 0

View File

@@ -0,0 +1,56 @@
#!/bin/bash
#
# Process Terminator Script for macOS
# This script forcibly terminates a process and all its descendant processes
#
# Check if PID argument is provided
if [ $# -ne 1 ]; then
echo "Usage: $0 <PID>"
exit 1
fi
PID=$1
# Validate that input is a valid PID (numeric)
if ! [[ "$PID" =~ ^[0-9]+$ ]]; then
echo "Error: The PID must be a numeric value"
exit 1
fi
# Check if the process exists - using kill -0 which is more reliable on macOS
if ! kill -0 $PID 2>/dev/null; then
echo "Warning: No process found with PID $PID"
exit 0
fi
# Recursive function to find and kill all descendant processes
kill_descendants() {
local parent=$1
# Use ps -axo for macOS to ensure all processes are included
local children=$(ps -axo pid=,ppid= | awk -v ppid=$parent '$2==ppid {print $1}')
echo "Processing children of PID: $parent..."
for child in $children; do
kill_descendants "$child"
echo "Terminating child process: $child"
kill -9 "$child" 2>/dev/null || true
done
}
echo "============================================"
echo "Starting termination of process $PID and all its descendants"
echo "============================================"
# Find and kill all descendant processes
kill_descendants "$PID"
# Finally kill the main process
echo "Terminating main process: $PID"
kill -9 "$PID" 2>/dev/null || true
echo "============================================"
echo "Process $PID and all its descendants have been terminated"
echo "============================================"
exit 0

View File

@@ -28,6 +28,8 @@
<EmbeddedResource Include="Sample\custom_routing_white" />
<EmbeddedResource Include="Sample\dns_singbox_normal" />
<EmbeddedResource Include="Sample\dns_v2ray_normal" />
<EmbeddedResource Include="Sample\kill_as_sudo_linux_sh" />
<EmbeddedResource Include="Sample\kill_as_sudo_osx_sh" />
<EmbeddedResource Include="Sample\pac" />
<EmbeddedResource Include="Sample\proxy_set_linux_sh" />
<EmbeddedResource Include="Sample\proxy_set_osx_sh" />

View File

@@ -519,7 +519,7 @@ public class CoreConfigSingboxService
{
try
{
var listen = "::";
var listen = "0.0.0.0";
singboxConfig.inbounds = [];
if (!_config.TunModeItem.EnableTun

View File

@@ -64,10 +64,9 @@ public partial class SudoPasswordInputView : UserControl
{
try
{
// Use sudo -S echo command to verify password
// Use sudo echo command to verify password
var arg = new List<string>() { "-c", "sudo -S echo SUDO_CHECK" };
var result = await CliWrap.Cli
.Wrap(Global.LinuxBash)
var result = await CliWrap.Cli.Wrap(Global.LinuxBash)
.WithArguments(arg)
.WithStandardInputPipe(CliWrap.PipeSource.FromString(password))
.ExecuteBufferedAsync();