Compare commits

...

54 commits

Author SHA1 Message Date
DHR60
faa540b929 Updates translations 2025-07-31 10:54:43 +08:00
DHR60
25cb376083 Updates sing-box documentation link 2025-07-31 10:54:43 +08:00
DHR60
7c01712e64 Fixes 2025-07-31 10:54:43 +08:00
DHR60
938ef6ccc4 Adds anytls reality support 2025-07-31 10:54:43 +08:00
DHR60
c526949fcb Deletes Duplicate Rules 2025-07-31 10:54:43 +08:00
DHR60
3d4ef78874 Adds sing-box DomainStrategy support 2025-07-31 10:54:43 +08:00
DHR60
1ba04f1cc6 Adds tag resolver supports 2025-07-31 10:54:43 +08:00
DHR60
8e1f15f415 Support sing-box hosts 2025-07-31 10:54:43 +08:00
DHR60
eff14326a5 Removes Wireguard listen port 2025-07-31 10:54:43 +08:00
DHR60
6848cd1e3e Adds properties to Rule4Sbox class 2025-07-31 10:54:43 +08:00
DHR60
72fcc590bd Improves DNS address parsing in Singbox
DNS type, host, port, and path
2025-07-31 10:54:43 +08:00
DHR60
3f4bb59c15 Removes direct clash_mode domain strategy 2025-07-31 10:54:43 +08:00
DHR60
a9c3b9438c Fixes wrong field 2025-07-31 10:54:43 +08:00
DHR60
1ad3c10619 fix singbox endpoints proxy chain not work 2025-07-31 10:54:43 +08:00
DHR60
9dba695cbb Fixes config generation 2025-07-31 10:54:43 +08:00
DHR60
02d9a87038 Refactors DNS address parsing 2025-07-31 10:54:43 +08:00
DHR60
4e808c8ec1 Adds IPv4 preference to DNS configurations
对应原dns.servers[].strategy = prefer_ipv4
2025-07-31 10:54:43 +08:00
DHR60
aa55c7d552 Adds Sing-box legacy DNS config support 2025-07-31 10:54:43 +08:00
DHR60
17269993f6 Utils.GetFreePort() default port to be zero 2025-07-31 10:54:43 +08:00
DHR60
532c5d3fb9 support Wireguard endpoint
Refactors Singbox config classes for dial fields
2025-07-31 10:54:43 +08:00
DHR60
d8bfc809c8 Fetches DNS strategy for domain resolution 2025-07-31 10:54:43 +08:00
DHR60
c0a027faa3 Enables dhcp interface configuration 2025-07-31 10:54:43 +08:00
DHR60
aee4f8daa7 Simplifies local DNS address handling 2025-07-31 10:54:43 +08:00
DHR60
01f7b342da add anytls support 2025-07-31 10:54:42 +08:00
DHR60
f5750e15cc Improves geoip rule handling in singbox 2025-07-31 10:54:42 +08:00
DHR60
d6bfd199a2 Adds Google cn dns rules 2025-07-31 10:54:42 +08:00
DHR60
2418a2ac14 Migrating to singbox 1.12 support 2025-07-31 10:54:42 +08:00
DHR60
8e59539b78 Removes unnecessary sniffer 2025-07-31 10:54:42 +08:00
DHR60
355e435ec4 Migrating to singbox 1.11 support 2025-07-31 10:54:42 +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
2dust
47c509faf6 up 7.13.3 2025-07-27 10:59:23 +08:00
2dust
8704942209 Improve sudo password interaction experience 2025-07-27 10:56:58 +08:00
2dust
e8cdc29bb5 Add sudo password verification success message prompt 2025-07-26 20:55:55 +08:00
DHR60
191a7a6574
Fixes Hysteria2 ports (#7649) 2025-07-26 15:07:07 +08:00
2dust
ad5d21db5a Upgrade Downloader package 2025-07-20 15:06:08 +08:00
2dust
569e939492 Optimizing and improving code 2025-07-20 14:16:19 +08:00
2dust
6a17c539d1 up 7.13.2 2025-07-18 20:07:10 +08:00
2dust
f8a4f946e4 Fixed the issue of missing files when updating GeoFiles
https://github.com/2dust/v2rayN/issues/7585
2025-07-18 19:56:18 +08:00
trojan-uma
0715fa85ce
改进 zh-Hans 描述 (#7579)
* 统一 zh-Hans 文字描述的括号

* 改进描述
2025-07-16 20:35:09 +08:00
2dust
1360051f0c Improve and optimize 2025-07-15 20:17:01 +08:00
2dust
42c4f9a6c6 Bug fix
https://github.com/2dust/v2rayN/issues/7582
2025-07-15 18:37:10 +08:00
2dust
11691d0128 up 7.13.1 2025-07-14 16:32:48 +08:00
2dust
26fe9c63a3 Bug fix
https://github.com/2dust/v2rayN/issues/7537
2025-07-14 16:32:10 +08:00
2dust
30cd033b42 up 7.13.0 2025-07-14 13:23:37 +08:00
2dust
e21c0b4d62 The outbound tag of the route rule can enter a config remarks
https://github.com/2dust/v2rayN/issues/7537
2025-07-13 20:25:53 +08:00
68 changed files with 1486 additions and 793 deletions

View file

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.12.7</Version>
<Version>7.13.4</Version>
</PropertyGroup>
<PropertyGroup>

View file

@ -10,7 +10,7 @@
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.2" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.2" />
<PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="3.3.4" />
<PackageVersion Include="Downloader" Version="4.0.2" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
@ -18,11 +18,11 @@
<PackageVersion Include="ReactiveUI" Version="20.4.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.8" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.8" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.9" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.9" />
<PackageVersion Include="Splat.NLog" Version="15.4.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.1" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />

View file

@ -26,7 +26,7 @@ public class DownloaderHelper
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
MaxTryAgainOnFailure = 2,
RequestConfiguration =
{
Headers = headers,
@ -64,7 +64,7 @@ public class DownloaderHelper
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
MaxTryAgainOnFailure = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,
@ -135,7 +135,7 @@ public class DownloaderHelper
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
MaxTryAgainOnFailure = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,

View file

@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common;
public static class StringEx
public static class Extension
{
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{
@ -79,4 +79,9 @@ public static class StringEx
{
return int.TryParse(value, out var result) ? result : defaultValue;
}
public static List<string> AppendEmpty(this IEnumerable<string> source)
{
return source.Concat(new[] { string.Empty }).ToList();
}
}

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

@ -323,6 +323,14 @@ public class Utils
return text.Replace("", ",").Replace(Environment.NewLine, ",");
}
public static List<string> GetEnumNames<TEnum>() where TEnum : Enum
{
return Enum.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(e => e.ToString())
.ToList();
}
#endregion
#region
@ -382,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;
@ -427,11 +466,11 @@ public class Utils
return false;
}
public static int GetFreePort(int defaultPort = 9090)
public static int GetFreePort(int defaultPort = 0)
{
try
{
if (!Utils.PortInUse(defaultPort))
if (!(defaultPort == 0 || Utils.PortInUse(defaultPort)))
{
return defaultPort;
}

View file

@ -11,5 +11,6 @@ public enum EConfigType
Hysteria2 = 7,
TUIC = 8,
WireGuard = 9,
HTTP = 10
HTTP = 10,
Anytls = 11
}

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";
@ -167,7 +169,8 @@ public class Global
{ EConfigType.Trojan, "trojan://" },
{ EConfigType.Hysteria2, "hysteria2://" },
{ EConfigType.TUIC, "tuic://" },
{ EConfigType.WireGuard, "wireguard://" }
{ EConfigType.WireGuard, "wireguard://" },
{ EConfigType.Anytls, "anytls://" }
};
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
@ -180,7 +183,8 @@ public class Global
{ EConfigType.Trojan, "trojan" },
{ EConfigType.Hysteria2, "hysteria2" },
{ EConfigType.TUIC, "tuic" },
{ EConfigType.WireGuard, "wireguard" }
{ EConfigType.WireGuard, "wireguard" },
{ EConfigType.Anytls, "anytls" }
};
public static readonly List<string> VmessSecurities =
@ -528,5 +532,12 @@ public class Global
@""
];
public static readonly List<string> OutboundTags =
[
ProxyTag,
DirectTag,
BlockTag
];
#endregion const
}

View file

@ -261,6 +261,7 @@ public class ConfigHandler
EConfigType.Hysteria2 => await AddHysteria2Server(config, item),
EConfigType.TUIC => await AddTuicServer(config, item),
EConfigType.WireGuard => await AddWireguardServer(config, item),
EConfigType.Anytls => await AddAnytlsServer(config, item),
_ => -1,
};
return ret;
@ -785,6 +786,35 @@ public class ConfigHandler
return 0;
}
/// <summary>
/// Add or edit a Anytls server
/// Validates and processes Anytls-specific settings
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="profileItem">Anytls profile to add</param>
/// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddAnytlsServer(Config config, ProfileItem profileItem, bool toFile = true)
{
profileItem.ConfigType = EConfigType.Anytls;
profileItem.CoreType = ECoreType.sing_box;
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Id = profileItem.Id.TrimEx();
profileItem.Security = profileItem.Security.TrimEx();
profileItem.Network = string.Empty;
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
profileItem.StreamSecurity = Global.StreamSecurity;
}
if (profileItem.Id.IsNullOrEmpty())
{
return -1;
}
await AddServerCommon(config, profileItem, toFile);
return 0;
}
/// <summary>
/// Sort the server list by the specified column
/// Updates the sort order in the profile extension data
@ -1294,6 +1324,7 @@ public class ConfigHandler
EConfigType.Hysteria2 => await AddHysteria2Server(config, profileItem, false),
EConfigType.TUIC => await AddTuicServer(config, profileItem, false),
EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false),
EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false),
_ => -1,
};

View file

@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Text;
using CliWrap;
using CliWrap.Buffered;
namespace ServiceLib.Handler;
@ -11,6 +12,7 @@ public class CoreAdminHandler
private Config _config;
private Action<bool, string>? _updateFunc;
private int _linuxSudoPid = -1;
private const string _tag = "CoreAdminHandler";
public async Task Init(Config config, Action<bool, string> updateFunc)
{
@ -20,6 +22,8 @@ public class CoreAdminHandler
}
_config = config;
_updateFunc = updateFunc;
await Task.CompletedTask;
}
private void UpdateFunc(bool notify, string msg)
@ -29,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()
{
@ -44,33 +51,26 @@ public class CoreAdminHandler
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
}
};
proc.OutputDataReceived += (sender, e) =>
void dataHandler(object sender, DataReceivedEventArgs e)
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
}
proc.OutputDataReceived += dataHandler;
proc.ErrorDataReceived += dataHandler;
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
@ -92,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 {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
return shFilePath;
}
}

View file

@ -101,7 +101,7 @@ public class CoreHandler
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) ? ECoreType.sing_box : ECoreType.Xray;
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray;
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
@ -280,20 +280,15 @@ public class CoreHandler
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
void dataHandler(object sender, DataReceivedEventArgs e)
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
}
proc.OutputDataReceived += dataHandler;
proc.ErrorDataReceived += dataHandler;
}
proc.Start();

View file

@ -0,0 +1,49 @@
using static QRCoder.PayloadGenerator;
namespace ServiceLib.Handler.Fmt;
public class AnytlsFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
var parsedUrl = Utils.TryUri(str);
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
ConfigType = EConfigType.Anytls,
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
item.Id = rawUserInfo;
var query = Utils.ParseQueryString(parsedUrl.Query);
_ = ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var pw = item.Id;
var dicQuery = new Dictionary<string, string>();
_ = GetStdTransport(item, Global.None, ref dicQuery);
return ToUri(EConfigType.Anytls, item.Address, item.Port, pw, dicQuery, remark);
}
}

View file

@ -18,6 +18,7 @@ public class FmtHandler
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
EConfigType.TUIC => TuicFmt.ToUri(item),
EConfigType.WireGuard => WireguardFmt.ToUri(item),
EConfigType.Anytls => AnytlsFmt.ToUri(item),
_ => null,
};
@ -75,6 +76,10 @@ public class FmtHandler
{
return WireguardFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Anytls]))
{
return AnytlsFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;

View file

@ -24,7 +24,7 @@ public class Hysteria2Fmt : BaseFmt
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
item.Ports = Utils.UrlDecode(query["mport"] ?? "").Replace('-', ':');
item.Ports = Utils.UrlDecode(query["mport"] ?? "");
return item;
}

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

@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models;
public class SingboxConfig
@ -6,6 +8,7 @@ public class SingboxConfig
public Dns4Sbox? dns { get; set; }
public List<Inbound4Sbox> inbounds { get; set; }
public List<Outbound4Sbox> outbounds { get; set; }
public List<Endpoints4Sbox>? endpoints { get; set; }
public Route4Sbox route { get; set; }
public Experimental4Sbox? experimental { get; set; }
}
@ -29,7 +32,6 @@ public class Dns4Sbox
public bool? independent_cache { get; set; }
public bool? reverse_mapping { get; set; }
public string? client_subnet { get; set; }
public Fakeip4Sbox? fakeip { get; set; }
}
public class Route4Sbox
@ -37,6 +39,7 @@ public class Route4Sbox
public bool? auto_detect_interface { get; set; }
public List<Rule4Sbox> rules { get; set; }
public List<Ruleset4Sbox>? rule_set { get; set; }
public string? final { get; set; }
}
[Serializable]
@ -49,6 +52,7 @@ public class Rule4Sbox
public string? mode { get; set; }
public bool? ip_is_private { get; set; }
public string? client_subnet { get; set; }
public int? rewrite_ttl { get; set; }
public bool? invert { get; set; }
public string? clash_mode { get; set; }
public List<string>? inbound { get; set; }
@ -67,6 +71,27 @@ public class Rule4Sbox
public List<string>? process_name { get; set; }
public List<string>? rule_set { get; set; }
public List<Rule4Sbox>? rules { get; set; }
public string? action { get; set; }
public string? strategy { get; set; }
public List<string>? sniffer { get; set; }
public string? rcode { get; set; }
public List<object>? query_type { get; set; }
public List<string>? answer { get; set; }
public List<string>? ns { get; set; }
public List<string>? extra { get; set; }
public string? method { get; set; }
public bool? no_drop { get; set; }
public bool? source_ip_is_private { get; set; }
public bool? ip_accept_any { get; set; }
public int? source_port { get; set; }
public List<string>? source_port_range { get; set; }
public List<string>? network_type { get; set; }
public bool? network_is_expensive { get; set; }
public bool? network_is_constrained { get; set; }
public List<string>? wifi_ssid { get; set; }
public List<string>? wifi_bssid { get; set; }
public bool? rule_set_ip_cidr_match_source { get; set; }
public bool? rule_set_ip_cidr_accept_empty { get; set; }
}
[Serializable]
@ -76,7 +101,6 @@ public class Inbound4Sbox
public string tag { get; set; }
public string listen { get; set; }
public int? listen_port { get; set; }
public string? domain_strategy { get; set; }
public string interface_name { get; set; }
public List<string>? address { get; set; }
public int? mtu { get; set; }
@ -84,8 +108,6 @@ public class Inbound4Sbox
public bool? strict_route { get; set; }
public bool? endpoint_independent_nat { get; set; }
public string? stack { get; set; }
public bool? sniff { get; set; }
public bool? sniff_override_destination { get; set; }
public List<User4Sbox> users { get; set; }
}
@ -95,10 +117,8 @@ public class User4Sbox
public string password { get; set; }
}
public class Outbound4Sbox
public class Outbound4Sbox : BaseServer4Sbox
{
public string type { get; set; }
public string tag { get; set; }
public string? server { get; set; }
public int? server_port { get; set; }
public List<string>? server_ports { get; set; }
@ -113,7 +133,6 @@ public class Outbound4Sbox
public int? recv_window_conn { get; set; }
public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; }
public string? detour { get; set; }
public string? method { get; set; }
public string? username { get; set; }
public string? password { get; set; }
@ -121,21 +140,36 @@ public class Outbound4Sbox
public string? version { get; set; }
public string? network { get; set; }
public string? packet_encoding { get; set; }
public List<string>? local_address { get; set; }
public string? private_key { get; set; }
public string? peer_public_key { get; set; }
public List<int>? reserved { get; set; }
public int? mtu { get; set; }
public string? plugin { get; set; }
public string? plugin_opts { get; set; }
public Tls4Sbox? tls { get; set; }
public Multiplex4Sbox? multiplex { get; set; }
public Transport4Sbox? transport { get; set; }
public HyObfs4Sbox? obfs { get; set; }
public List<string>? outbounds { get; set; }
public bool? interrupt_exist_connections { get; set; }
}
public class Endpoints4Sbox : BaseServer4Sbox
{
public bool? system { get; set; }
public string? name { get; set; }
public int? mtu { get; set; }
public List<string> address { get; set; }
public string private_key { get; set; }
public int? listen_port { get; set; }
public string? udp_timeout { get; set; }
public int? workers { get; set; }
public List<Peer4Sbox> peers { get; set; }
}
public class Peer4Sbox
{
public string address { get; set; }
public int port { get; set; }
public string public_key { get; set; }
public string? pre_shared_key { get; set; }
public List<string> allowed_ips { get; set; }
public int? persistent_keepalive_interval { get; set; }
public List<int> reserved { get; set; }
}
public class Tls4Sbox
{
public bool enabled { get; set; }
@ -191,15 +225,25 @@ public class HyObfs4Sbox
public string? password { get; set; }
}
public class Server4Sbox
public class Server4Sbox : BaseServer4Sbox
{
public string? tag { get; set; }
public string? inet4_range { get; set; }
public string? inet6_range { get; set; }
public string? client_subnet { get; set; }
public string? server { get; set; }
public new string? domain_resolver { get; set; }
[JsonPropertyName("interface")] public string? Interface { get; set; }
public int? server_port { get; set; }
public string? path { get; set; }
public Headers4Sbox? headers { get; set; }
// public List<string>? path { get; set; } // hosts
public Dictionary<string, object>? predefined { get; set; }
// Deprecated
public string? address { get; set; }
public string? address_resolver { get; set; }
public string? address_strategy { get; set; }
public string? strategy { get; set; }
public string? detour { get; set; }
public string? client_subnet { get; set; }
// Deprecated End
}
public class Experimental4Sbox
@ -229,13 +273,6 @@ public class Stats4Sbox
public List<string>? users { get; set; }
}
public class Fakeip4Sbox
{
public bool enabled { get; set; }
public string inet4_range { get; set; }
public string inet6_range { get; set; }
}
public class CacheFile4Sbox
{
public bool enabled { get; set; }
@ -254,3 +291,33 @@ public class Ruleset4Sbox
public string? download_detour { get; set; }
public string? update_interval { get; set; }
}
public abstract class DialFields4Sbox
{
public string? detour { get; set; }
public string? bind_interface { get; set; }
public string? inet4_bind_address { get; set; }
public string? inet6_bind_address { get; set; }
public int? routing_mark { get; set; }
public bool? reuse_addr { get; set; }
public string? netns { get; set; }
public string? connect_timeout { get; set; }
public bool? tcp_fast_open { get; set; }
public bool? tcp_multi_path { get; set; }
public bool? udp_fragment { get; set; }
public Rule4Sbox? domain_resolver { get; set; } // or string
public string? network_strategy { get; set; }
public List<string>? network_type { get; set; }
public List<string>? fallback_network_type { get; set; }
public string? fallback_delay { get; set; }
public Tls4Sbox? tls { get; set; }
public Multiplex4Sbox? multiplex { get; set; }
public Transport4Sbox? transport { get; set; }
public HyObfs4Sbox? obfs { get; set; }
}
public abstract class BaseServer4Sbox : DialFields4Sbox
{
public string type { get; set; }
public string tag { get; set; }
}

View file

@ -654,6 +654,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Add [Anytls] Configuration 的本地化字符串。
/// </summary>
public static string menuAddAnytlsServer {
get {
return ResourceManager.GetString("menuAddAnytlsServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
/// </summary>
@ -2202,6 +2211,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Incorrect password, please try again. 的本地化字符串。
/// </summary>
public static string SudoIncorrectPasswordTip {
get {
return ResourceManager.GetString("SudoIncorrectPasswordTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 Address 的本地化字符串。
/// </summary>
@ -2724,6 +2742,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Can fill in the configuration remarks, please make sure it exist and are unique 的本地化字符串。
/// </summary>
public static string TbRuleOutboundTagTip {
get {
return ResourceManager.GetString("TbRuleOutboundTagTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 Encryption method (security) 的本地化字符串。
/// </summary>
@ -3184,7 +3211,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart. 的本地化字符串。
/// 查找类似 The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart. 的本地化字符串。
/// </summary>
public static string TbSettingsLinuxSudoPasswordTip {
get {

View file

@ -1318,7 +1318,7 @@
<value>رمز عبور sudo سیستم</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>رمز عبوری که وارد کرده اید تایید نمی شود، بنابراین مطمئن شوید که آن را به درستی وارد کرده اید. اگر برنامه به دلیل ورودی نادرست به درستی کار نمی کند، لطفاً برنامه را مجدداً راه اندازی کنید. رمز عبور ذخیره نمی شود و پس از هر بار راه اندازی مجدد باید آن را دوباره وارد کنید.</value>
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
</data>
<data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*حالت xhttp</value>
@ -1392,4 +1392,13 @@
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>URL آزمایش اطلاعات اتصال فعلی</value>
</data>
<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="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>Add [Anytls] Configuration</value>
</data>
</root>

View file

@ -1318,7 +1318,7 @@
<value>Rendszer sudo jelszó</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart.</value>
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
</data>
<data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*xhttp mód</value>
@ -1392,4 +1392,13 @@
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>Current connection info test URL</value>
</data>
<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="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>[Anytls] szerver hozzáadása</value>
</data>
</root>

View file

@ -1318,7 +1318,7 @@
<value>System sudo password</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart.</value>
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
</data>
<data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*xhttp mode</value>
@ -1392,4 +1392,13 @@
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>Current connection info test URL</value>
</data>
<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="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>Add [Anytls] Configuration</value>
</data>
</root>

View file

@ -1318,7 +1318,7 @@
<value>Пароль sudo системы</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>Введенный вами пароль не может быть подтвержден, поэтому убедитесь, что вы ввели его правильно. Если приложение не работает должным образом из-за неправильного ввода, то перезапустите его. Пароль не будет сохранен, и вам придется вводить его заново после каждого перезапуска</value>
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
</data>
<data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*XHTTP-режим</value>
@ -1392,4 +1392,13 @@
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>URL для тестирования текущего соединения</value>
</data>
<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="SudoIncorrectPasswordTip" xml:space="preserve">
<value>Incorrect password, please try again.</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>Добавить сервер [Anytls]</value>
</data>
</root>

View file

@ -703,7 +703,7 @@
<value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号 (;) 分隔。</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>显示实时速度(需重启)</value>
<value>显示实时速度 (需重启)</value>
</data>
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
<value>去重时保留序号较小的项</value>
@ -739,7 +739,7 @@
<value>开机启动 (可能会不成功)</value>
</data>
<data name="TbSettingsStatistics" xml:space="preserve">
<value>启用流量统计(需重启)</value>
<value>启用流量统计 (需重启)</value>
</data>
<data name="TbSettingsSubConvert" xml:space="preserve">
<value>订阅转换网址 (可选)</value>
@ -886,7 +886,7 @@
<value>仅限路由 (routeOnly)</value>
</data>
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
<value>请勿将代理服务器用于本地Intranet地址</value>
<value>请勿将代理服务器用于本地 (Intranet) 地址</value>
</data>
<data name="menuMixedTestServer" xml:space="preserve">
<value>一键多线程测试延迟和速度 (Ctrl+E)</value>
@ -919,7 +919,7 @@
<value>移至订阅分组</value>
</data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>启用配置文件拖放排序(需重启)</value>
<value>启用配置文件拖放排序 (需重启)</value>
</data>
<data name="TbAutoRefresh" xml:space="preserve">
<value>自动刷新</value>
@ -946,10 +946,10 @@
<value>仅对 tcp/http、ws 协议生效</value>
</data>
<data name="TbSettingsCurrentFontFamily" xml:space="preserve">
<value>当前字体(需重启)</value>
<value>当前字体 (需重启)</value>
</data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>拷贝字体 TTF/TTC 文件到目录 guiFonts重启设置</value>
<value>拷贝字体 TTF/TTC 文件到目录 guiFonts重启生效</value>
</data>
<data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>Pac 端口 = +3Xray API 端口 = +4mihomo API 端口 = +5</value>
@ -979,10 +979,10 @@
<value>SpiderX</value>
</data>
<data name="TbSettingsEnableHWA" xml:space="preserve">
<value>启用硬件加速(需重启)</value>
<value>启用硬件加速 (需重启)</value>
</data>
<data name="SpeedtestingWait" xml:space="preserve">
<value>等待测试中(按 ESC 终止)...</value>
<value>等待测试中 (按 ESC 终止)...</value>
</data>
<data name="TipDisplayLog" xml:space="preserve">
<value>当有异常断流时请关闭</value>
@ -1075,7 +1075,7 @@
<value>Reserved (2,3,4)</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address (Ipv4,Ipv6)</value>
<value>Address (IPv4,IPv6)</value>
</data>
<data name="TbPath7" xml:space="preserve">
<value>混淆密码 (obfs password)</value>
@ -1105,10 +1105,10 @@
<value>使用 Xray 且非 Tun 模式启用,和分组前置代理冲突</value>
</data>
<data name="TbSettingsEnableFragment" xml:space="preserve">
<value>启用分片Fragment</value>
<value>启用分片 (Fragment)</value>
</data>
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
<value>启用 sing-box(规则集文件)的缓存文件</value>
<value>启用 sing-box (规则集文件) 的缓存文件</value>
</data>
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
<value>自定义 sing-box rule-set</value>
@ -1201,7 +1201,7 @@
<value>Outbound 默认解析策略</value>
</data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>主界面布局方向(需重启)</value>
<value>主界面布局方向 (需重启)</value>
</data>
<data name="TbSettingsDomainDNSAddress" xml:space="preserve">
<value>Outbound 域名解析地址</value>
@ -1303,7 +1303,7 @@
<value>请不要使用不安全的 HTTP 协议订阅地址</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安装字体到系统中,选择或填入字体名称,重启设置</value>
<value>安装字体到系统中,选择或填入字体名称,重启生效</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>是否确定退出?</value>
@ -1315,10 +1315,10 @@
<value>系统的 sudo 密码</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>输入的密码无法校验,所以请确保输入正确。如果因为输入错误导致无法正常运行时,请重启本应用。 密码不会存储,每次重启后都需要再次输入。</value>
<value>密码将调用命令行校验,如果因为校验错误导致无法正常运行时,请重启本应用。 密码不会存储,每次重启后都需要再次输入。</value>
</data>
<data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*xhttp 模式</value>
<value>*XHTTP 模式</value>
</data>
<data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra 原始 JSON格式 { XHTTPObject }</value>
@ -1339,7 +1339,7 @@
<value>开启第二个本地监听端口</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks本地端口socks2第二个本地端口socks3局域网端口</value>
<value>Socks本地端口Socks2第二个本地端口Socks3局域网端口</value>
</data>
<data name="TbSettingsTheme" xml:space="preserve">
<value>主题</value>
@ -1372,13 +1372,13 @@
<value>多配置文件随机 Xray</value>
</data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多配置文件负载均衡  Xray</value>
<value>多配置文件负载均衡 Xray</value>
</data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
<value>多配置文件最低延迟  Xray</value>
<value>多配置文件最低延迟 Xray</value>
</data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多配置文件最稳定  Xray</value>
<value>多配置文件最稳定 Xray</value>
</data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多配置文件最低延迟 sing-box</value>
@ -1389,4 +1389,13 @@
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>当前连接信息测试地址</value>
</data>
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填写配置文件别名,请确保存在并唯一</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密码错误,请重试。</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>添加 [Anytls] 配置文件</value>
</data>
</root>

View file

@ -1315,7 +1315,7 @@
<value>系統的 sudo 密碼</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>輸入的密碼無法校驗,所以請確保輸入正確。如果因為輸入錯誤導致無法正常運作時,請重新啟動本應用。 密碼不會儲存,每次重啟後都需要再次輸入。</value>
<value>密碼將調用命令行校驗,如果因為校驗錯誤導致無法正常運行時,請重啟本應用。密碼不會存儲,每次重啟後都需要再次輸入。</value>
</data>
<data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*xhttp 模式</value>
@ -1389,4 +1389,13 @@
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>目前連接資訊測試地址</value>
</data>
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填寫設定檔別名,請確保存在並唯一</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密碼錯誤,請重試。</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>新增 [Anytls] 設定檔</value>
</data>
</root>

View file

@ -1,4 +1,4 @@
{
{
"log": {
"level": "debug",
"timestamp": true
@ -14,22 +14,10 @@
{
"type": "direct",
"tag": "direct"
},
{
"type": "block",
"tag": "block"
},
{
"tag": "dns_out",
"type": "dns"
}
],
"route": {
"rules": [
{
"protocol": [ "dns" ],
"outbound": "dns_out"
}
]
}
}

View file

@ -2,28 +2,33 @@
"servers": [
{
"tag": "remote",
"address": "tcp://8.8.8.8",
"strategy": "prefer_ipv4",
"type": "tcp",
"server": "8.8.8.8",
"detour": "proxy"
},
{
"tag": "local",
"address": "223.5.5.5",
"strategy": "prefer_ipv4",
"detour": "direct"
},
{
"tag": "block",
"address": "rcode://success"
"type": "udp",
"server": "223.5.5.5"
}
],
"rules": [
{
"domain_suffix": [
"googleapis.cn",
"gstatic.com"
],
"server": "remote",
"strategy": "prefer_ipv4"
},
{
"rule_set": [
"geosite-cn"
],
"server": "local"
"server": "local",
"strategy": "prefer_ipv4"
}
],
"final": "remote"
"final": "remote",
"strategy": "prefer_ipv4"
}

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

@ -2,29 +2,33 @@
"servers": [
{
"tag": "remote",
"address": "tcp://8.8.8.8",
"strategy": "prefer_ipv4",
"type": "tcp",
"server": "8.8.8.8",
"detour": "proxy"
},
{
"tag": "local",
"address": "223.5.5.5",
"strategy": "prefer_ipv4",
"detour": "direct"
},
{
"tag": "block",
"address": "rcode://success"
"type": "udp",
"server": "223.5.5.5"
}
],
"rules": [
{
"rule_set": [
"geosite-cn",
"geosite-geolocation-cn"
"domain_suffix": [
"googleapis.cn",
"gstatic.com"
],
"server": "local"
"server": "remote",
"strategy": "prefer_ipv4"
},
{
"rule_set": [
"geosite-cn"
],
"server": "local",
"strategy": "prefer_ipv4"
}
],
"final": "remote"
}
"final": "remote",
"strategy": "prefer_ipv4"
}

View file

@ -8,13 +8,13 @@
139,
5353
],
"outbound": "block"
"action": "reject"
},
{
"ip_cidr": [
"224.0.0.0/3",
"ff00::/8"
],
"outbound": "block"
"action": "reject"
}
]

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

@ -1,6 +1,9 @@
using System.Data;
using System.Net;
using System.Net.NetworkInformation;
using System.Reactive;
using DynamicData;
using ServiceLib.Models;
namespace ServiceLib.Services.CoreConfig;
@ -53,7 +56,18 @@ public class CoreConfigSingboxService
await GenInbounds(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds.First());
if (node.ConfigType == EConfigType.WireGuard)
{
singboxConfig.outbounds.RemoveAt(0);
var endpoints = new Endpoints4Sbox();
await GenEndpoint(node, endpoints);
endpoints.tag = Global.ProxyTag;
singboxConfig.endpoints = new() { endpoints };
}
else
{
await GenOutbound(node, singboxConfig.outbounds.First());
}
await GenMoreOutbounds(node, singboxConfig);
@ -202,16 +216,29 @@ public class CoreConfigSingboxService
continue;
}
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(item, outbound);
outbound.tag = Global.ProxyTag + inbound.listen_port.ToString();
singboxConfig.outbounds.Add(outbound);
var server = await GenServer(item);
if (server is null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
var tag = Global.ProxyTag + inbound.listen_port.ToString();
server.tag = tag;
if (server is Endpoints4Sbox endpoint)
{
singboxConfig.endpoints ??= new();
singboxConfig.endpoints.Add(endpoint);
}
else if (server is Outbound4Sbox outbound)
{
singboxConfig.outbounds.Add(outbound);
}
//rule
Rule4Sbox rule = new()
{
inbound = new List<string> { inbound.tag },
outbound = outbound.tag
outbound = tag
};
singboxConfig.route.rules.Add(rule);
}
@ -275,7 +302,18 @@ public class CoreConfigSingboxService
}
await GenLog(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds.First());
if (node.ConfigType == EConfigType.WireGuard)
{
singboxConfig.outbounds.RemoveAt(0);
var endpoints = new Endpoints4Sbox();
await GenEndpoint(node, endpoints);
endpoints.tag = Global.ProxyTag;
singboxConfig.endpoints = new() { endpoints };
}
else
{
await GenOutbound(node, singboxConfig.outbounds.First());
}
await GenMoreOutbounds(node, singboxConfig);
await GenDnsDomains(null, singboxConfig, null);
@ -519,7 +557,7 @@ public class CoreConfigSingboxService
{
try
{
var listen = "::";
var listen = "0.0.0.0";
singboxConfig.inbounds = [];
if (!_config.TunModeItem.EnableTun
@ -534,15 +572,6 @@ public class CoreConfigSingboxService
singboxConfig.inbounds.Add(inbound);
inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
inbound.sniff = _config.Inbound.First().SniffingEnabled;
inbound.sniff_override_destination = _config.Inbound.First().RouteOnly ? false : _config.Inbound.First().SniffingEnabled;
inbound.domain_strategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing.DomainStrategy4Singbox.IsNotEmpty())
{
inbound.domain_strategy = routing.DomainStrategy4Singbox;
}
if (_config.Inbound.First().SecondLocalPortEnabled)
{
@ -587,8 +616,6 @@ public class CoreConfigSingboxService
tunInbound.mtu = _config.TunModeItem.Mtu;
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
tunInbound.stack = _config.TunModeItem.Stack;
tunInbound.sniff = _config.Inbound.First().SniffingEnabled;
//tunInbound.sniff_override_destination = _config.inbound.First().routeOnly ? false : _config.inbound.First().sniffingEnabled;
if (_config.TunModeItem.EnableIPv6Address == false)
{
tunInbound.address = ["172.18.0.1/30"];
@ -621,6 +648,17 @@ public class CoreConfigSingboxService
outbound.server_port = node.Port;
outbound.type = Global.ProtocolTypes[node.ConfigType];
if (Utils.IsDomain(node.Address))
{
var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
var localDnsAddress = string.IsNullOrEmpty(item?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : item?.DomainDNSAddress;
outbound.domain_resolver = new()
{
server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver",
strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom
};
}
switch (node.ConfigType)
{
case EConfigType.VMess:
@ -706,12 +744,17 @@ public class CoreConfigSingboxService
outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null;
outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null;
if (node.Ports.IsNotEmpty())
if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
{
outbound.server_port = null;
outbound.server_ports = node.Ports.Split(',')
.Where(p => p.Trim().IsNotEmpty())
.Select(p => p.Replace('-', ':'))
.Select(p => p.Trim())
.Where(p => p.IsNotEmpty())
.Select(p =>
{
var port = p.Replace('-', ':');
return port.Contains(':') ? port : $"{port}:{port}";
})
.ToList();
outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null;
}
@ -725,13 +768,9 @@ public class CoreConfigSingboxService
outbound.congestion_control = node.HeaderType;
break;
}
case EConfigType.WireGuard:
case EConfigType.Anytls:
{
outbound.private_key = node.Id;
outbound.peer_public_key = node.PublicKey;
outbound.reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList();
outbound.local_address = Utils.String2List(node.RequestHost);
outbound.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt();
outbound.password = node.Id;
break;
}
}
@ -747,6 +786,76 @@ public class CoreConfigSingboxService
return 0;
}
private async Task<int> GenEndpoint(ProfileItem node, Endpoints4Sbox endpoint)
{
try
{
endpoint.address = Utils.String2List(node.RequestHost);
endpoint.type = Global.ProtocolTypes[node.ConfigType];
if (Utils.IsDomain(node.Address))
{
var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
var localDnsAddress = string.IsNullOrEmpty(item?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : item?.DomainDNSAddress;
endpoint.domain_resolver = new()
{
server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver",
strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom
};
}
switch (node.ConfigType)
{
case EConfigType.WireGuard:
{
var peer = new Peer4Sbox
{
public_key = node.PublicKey,
reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(),
address = node.Address,
port = node.Port,
// TODO default ["0.0.0.0/0", "::/0"]
allowed_ips = new() { "0.0.0.0/0", "::/0" },
};
endpoint.private_key = node.Id;
endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt();
endpoint.peers = new() { peer };
break;
}
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return await Task.FromResult(0);
}
private async Task<BaseServer4Sbox?> GenServer(ProfileItem node)
{
try
{
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
if (node.ConfigType == EConfigType.WireGuard)
{
var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound);
await GenEndpoint(node, endpoint);
return endpoint;
}
else
{
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(node, outbound);
return outbound;
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return await Task.FromResult<BaseServer4Sbox?>(null);
}
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
{
try
@ -913,7 +1022,8 @@ public class CoreConfigSingboxService
}
//current proxy
var outbound = singboxConfig.outbounds.First();
BaseServer4Sbox? outbound = singboxConfig.endpoints?.FirstOrDefault(t => t.tag == Global.ProxyTag) == null ? singboxConfig.outbounds.First() : null;
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
//Previous proxy
@ -922,17 +1032,32 @@ public class CoreConfigSingboxService
if (prevNode is not null
&& prevNode.ConfigType != EConfigType.Custom)
{
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(prevNode, prevOutbound);
prevOutboundTag = $"prev-{Global.ProxyTag}";
prevOutbound.tag = prevOutboundTag;
singboxConfig.outbounds.Add(prevOutbound);
var prevServer = await GenServer(prevNode);
prevServer.tag = prevOutboundTag;
if (prevServer is Endpoints4Sbox endpoint)
{
singboxConfig.endpoints ??= new();
singboxConfig.endpoints.Add(endpoint);
}
else if (prevServer is Outbound4Sbox outboundPrev)
{
singboxConfig.outbounds.Add(outboundPrev);
}
}
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
var nextServer = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
if (nextOutbound is not null)
if (nextServer is not null)
{
singboxConfig.outbounds.Insert(0, nextOutbound);
if (nextServer is Endpoints4Sbox endpoint)
{
singboxConfig.endpoints ??= new();
singboxConfig.endpoints.Insert(0, endpoint);
}
else if (nextServer is Outbound4Sbox outboundNext)
{
singboxConfig.outbounds.Insert(0, outboundNext);
}
}
}
catch (Exception ex)
@ -955,11 +1080,13 @@ public class CoreConfigSingboxService
}
var resultOutbounds = new List<Outbound4Sbox>();
var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints
var prevOutbounds = new List<Outbound4Sbox>(); // Separate list for prev outbounds
var prevEndpoints = new List<Endpoints4Sbox>(); // Separate list for prev endpoints
var proxyTags = new List<string>(); // For selector and urltest outbounds
// Cache for chain proxies to avoid duplicate generation
var nextProxyCache = new Dictionary<string, Outbound4Sbox?>();
var nextProxyCache = new Dictionary<string, BaseServer4Sbox?>();
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
int prevIndex = 0; // Index for prev outbounds
@ -971,19 +1098,18 @@ public class CoreConfigSingboxService
// Handle proxy chain
string? prevTag = null;
var currentOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
if (nextOutbound != null)
var currentServer = await GenServer(node);
var nextServer = nextProxyCache.GetValueOrDefault(node.Subid, null);
if (nextServer != null)
{
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
nextServer = JsonUtils.DeepCopy(nextServer);
}
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
// current proxy
await GenOutbound(node, currentOutbound);
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
proxyTags.Add(currentOutbound.tag);
currentServer.tag = $"{Global.ProxyTag}-{index}";
proxyTags.Add(currentServer.tag);
if (!node.Subid.IsNullOrEmpty())
{
@ -1006,18 +1132,32 @@ public class CoreConfigSingboxService
prevProxyTags[node.Subid] = prevTag;
}
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
nextServer = await GenChainOutbounds(subItem, currentServer, prevTag, nextServer);
if (!nextProxyCache.ContainsKey(node.Subid))
{
nextProxyCache[node.Subid] = nextOutbound;
nextProxyCache[node.Subid] = nextServer;
}
}
if (nextOutbound is not null)
if (nextServer is not null)
{
resultOutbounds.Add(nextOutbound);
if (nextServer is Endpoints4Sbox nextEndpoint)
{
resultEndpoints.Add(nextEndpoint);
}
else if (nextServer is Outbound4Sbox nextOutbound)
{
resultOutbounds.Add(nextOutbound);
}
}
if (currentServer is Endpoints4Sbox currentEndpoint)
{
resultEndpoints.Add(currentEndpoint);
}
else if (currentServer is Outbound4Sbox currentOutbound)
{
resultOutbounds.Add(currentOutbound);
}
resultOutbounds.Add(currentOutbound);
}
// Add urltest outbound (auto selection based on latency)
@ -1050,6 +1190,9 @@ public class CoreConfigSingboxService
resultOutbounds.AddRange(prevOutbounds);
resultOutbounds.AddRange(singboxConfig.outbounds);
singboxConfig.outbounds = resultOutbounds;
singboxConfig.endpoints ??= new List<Endpoints4Sbox>();
resultEndpoints.AddRange(singboxConfig.endpoints);
singboxConfig.endpoints = resultEndpoints;
}
catch (Exception ex)
{
@ -1071,7 +1214,7 @@ public class CoreConfigSingboxService
/// <returns>
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
/// </returns>
private async Task<Outbound4Sbox?> GenChainOutbounds(SubItem subItem, Outbound4Sbox outbound, string? prevOutboundTag, Outbound4Sbox? nextOutbound = null)
private async Task<BaseServer4Sbox?> GenChainOutbounds(SubItem subItem, BaseServer4Sbox outbound, string? prevOutboundTag, BaseServer4Sbox? nextOutbound = null)
{
try
{
@ -1087,11 +1230,7 @@ public class CoreConfigSingboxService
if (nextNode is not null
&& nextNode.ConfigType != EConfigType.Custom)
{
if (nextOutbound == null)
{
nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(nextNode, nextOutbound);
}
nextOutbound ??= await GenServer(nextNode);
nextOutbound.tag = outbound.tag;
outbound.tag = $"mid-{outbound.tag}";
@ -1110,7 +1249,7 @@ public class CoreConfigSingboxService
{
try
{
var dnsOutbound = "dns_out";
singboxConfig.route.final = Global.ProxyTag;
if (_config.TunModeItem.EnableTun)
{
@ -1126,7 +1265,7 @@ public class CoreConfigSingboxService
singboxConfig.route.rules.Add(new()
{
port = new() { 53 },
outbound = dnsOutbound,
action = "hijack-dns",
process_name = lstDnsExe
});
@ -1137,13 +1276,25 @@ public class CoreConfigSingboxService
});
}
if (!_config.Inbound.First().SniffingEnabled)
if (_config.Inbound.First().SniffingEnabled)
{
singboxConfig.route.rules.Add(new()
{
port = [53],
network = ["udp"],
outbound = dnsOutbound
action = "sniff"
});
singboxConfig.route.rules.Add(new()
{
protocol = new() { "dns" },
action = "hijack-dns"
});
}
else
{
singboxConfig.route.rules.Add(new()
{
port = new() { 53 },
network = new() { "udp" },
action = "hijack-dns"
});
}
@ -1158,7 +1309,24 @@ public class CoreConfigSingboxService
clash_mode = ERuleMode.Global.ToString()
});
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
{
domainStrategy = defaultRouting.DomainStrategy4Singbox;
}
var resolveRule = new Rule4Sbox
{
action = "resolve",
strategy = domainStrategy
};
if (_config.RoutingBasicItem.DomainStrategy == "IPOnDemand")
{
singboxConfig.route.rules.Add(resolveRule);
}
var routing = await ConfigHandler.GetDefaultRouting(_config);
var ipRules = new List<RulesItem>();
if (routing != null)
{
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
@ -1166,10 +1334,22 @@ public class CoreConfigSingboxService
{
if (item.Enabled)
{
await GenRoutingUserRule(item, singboxConfig.route.rules);
await GenRoutingUserRule(item, singboxConfig);
if (item.Ip != null && item.Ip.Count > 0)
{
ipRules.Add(item);
}
}
}
}
if (_config.RoutingBasicItem.DomainStrategy == "IPIfNonMatch")
{
singboxConfig.route.rules.Add(resolveRule);
foreach (var item in ipRules)
{
await GenRoutingUserRule(item, singboxConfig);
}
}
}
catch (Exception ex)
{
@ -1206,7 +1386,7 @@ public class CoreConfigSingboxService
lstDirectExe = new List<string>(directExeSet);
}
private async Task<int> GenRoutingUserRule(RulesItem item, List<Rule4Sbox> rules)
private async Task<int> GenRoutingUserRule(RulesItem item, SingboxConfig singboxConfig)
{
try
{
@ -1214,11 +1394,18 @@ public class CoreConfigSingboxService
{
return 0;
}
item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig);
var rules = singboxConfig.route.rules;
var rule = new Rule4Sbox()
var rule = new Rule4Sbox();
if (item.OutboundTag == "block")
{
outbound = item.OutboundTag,
};
rule.action = "reject";
}
else
{
rule.outbound = item.OutboundTag;
}
if (item.Port.IsNotEmpty())
{
@ -1342,29 +1529,67 @@ public class CoreConfigSingboxService
{
return false;
}
else if (address.StartsWith("geoip:!"))
{
return false;
}
else if (address.Equals("geoip:private"))
{
rule.ip_is_private = true;
}
else if (address.StartsWith("geoip:"))
{
if (rule.geoip is null)
{ rule.geoip = new(); }
rule.geoip ??= new();
rule.geoip?.Add(address.Substring(6));
}
else if (address.Equals("geoip:!private"))
{
rule.ip_is_private = false;
}
else if (address.StartsWith("geoip:!"))
{
rule.geoip ??= new();
rule.geoip?.Add(address.Substring(6));
rule.invert = true;
}
else
{
if (rule.ip_cidr is null)
{ rule.ip_cidr = new(); }
rule.ip_cidr ??= new();
rule.ip_cidr?.Add(address);
}
return true;
}
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, SingboxConfig singboxConfig)
{
if (Global.OutboundTags.Contains(outboundTag))
{
return outboundTag;
}
var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag);
if (node == null
|| node.ConfigType == EConfigType.Custom)
{
return Global.ProxyTag;
}
var server = await GenServer(node);
if (server is null)
{
return Global.ProxyTag;
}
server.tag = Global.ProxyTag + node.IndexId.ToString();
if (server is Endpoints4Sbox endpoint)
{
singboxConfig.endpoints ??= new();
singboxConfig.endpoints.Add(endpoint);
}
else if (server is Outbound4Sbox outbound)
{
singboxConfig.outbounds.Add(outbound);
}
return server.tag;
}
private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig)
{
try
@ -1387,7 +1612,14 @@ public class CoreConfigSingboxService
}
singboxConfig.dns = dns4Sbox;
await GenDnsDomains(node, singboxConfig, item);
if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty())
{
await GenDnsDomains(node, singboxConfig, item);
}
else
{
await GenDnsDomainsLegacy(node, singboxConfig, item);
}
}
catch (Exception ex)
{
@ -1402,6 +1634,75 @@ public class CoreConfigSingboxService
dns4Sbox.servers ??= [];
dns4Sbox.rules ??= [];
var tag = "local_resolver";
var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress;
if (localDnsAddress.StartsWith("tag://"))
{
tag = localDnsAddress.Substring(6);
var localDnsTag = "local_local";
dns4Sbox.servers.Add(new()
{
tag = localDnsTag,
type = "local"
});
dns4Sbox.rules.Insert(0, new()
{
server = localDnsTag,
clash_mode = ERuleMode.Direct.ToString()
});
}
else
{
var (dnsType, dnsHost, dnsPort, dnsPath) = ParseDnsAddress(localDnsAddress);
dns4Sbox.servers.Add(new()
{
tag = tag,
type = dnsType,
server = dnsHost,
Interface = dnsType == "dhcp" ? dnsHost : null,
server_port = dnsPort,
path = dnsPath
});
dns4Sbox.rules.Insert(0, new()
{
server = tag,
clash_mode = ERuleMode.Direct.ToString()
});
}
dns4Sbox.rules.Insert(0, new()
{
server = dns4Sbox.servers.Where(t => t.detour == Global.ProxyTag).Select(t => t.tag).FirstOrDefault() ?? "remote",
clash_mode = ERuleMode.Global.ToString()
});
//Tun2SocksAddress
if (_config.TunModeItem.EnableTun && node?.ConfigType == EConfigType.SOCKS && Utils.IsDomain(node?.Sni))
{
dns4Sbox.rules.Insert(0, new()
{
server = tag,
domain = [node?.Sni],
strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom
});
}
singboxConfig.dns = dns4Sbox;
return await Task.FromResult(0);
}
private async Task<int> GenDnsDomainsLegacy(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem)
{
var dns4Sbox = singboxConfig.dns ?? new();
dns4Sbox.servers ??= [];
dns4Sbox.rules ??= [];
var tag = "local_local";
dns4Sbox.servers.Add(new()
{
@ -1449,6 +1750,91 @@ public class CoreConfigSingboxService
return await Task.FromResult(0);
}
private (string type, string? host, int? port, string? path) ParseDnsAddress(string address)
{
string type = "udp";
string? host = null;
int? port = null;
string? path = null;
if (address is "local" or "localhost")
{
return ("local", null, null, null);
}
if (address.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase))
{
string interface_name = address.Substring(7);
return ("dhcp", interface_name == "auto" ? null : interface_name, null, null);
}
if (!address.Contains("://"))
{
// udp dns
host = address;
return (type, host, port, path);
}
try
{
int protocolEndIndex = address.IndexOf("://", StringComparison.Ordinal);
type = address.Substring(0, protocolEndIndex).ToLower();
var uri = new Uri(address);
host = uri.Host;
if (!uri.IsDefaultPort)
{
port = uri.Port;
}
if ((type == "https" || type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/")
{
path = uri.AbsolutePath;
}
}
catch (UriFormatException)
{
int protocolEndIndex = address.IndexOf("://", StringComparison.Ordinal);
if (protocolEndIndex > 0)
{
type = address.Substring(0, protocolEndIndex).ToLower();
string remaining = address.Substring(protocolEndIndex + 3);
int portIndex = remaining.IndexOf(':');
int pathIndex = remaining.IndexOf('/');
if (portIndex > 0)
{
host = remaining.Substring(0, portIndex);
string portPart = pathIndex > portIndex
? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1)
: remaining.Substring(portIndex + 1);
if (int.TryParse(portPart, out int parsedPort))
{
port = parsedPort;
}
}
else if (pathIndex > 0)
{
host = remaining.Substring(0, pathIndex);
}
else
{
host = remaining;
}
if (pathIndex > 0 && (type == "https" || type == "h3"))
{
path = remaining.Substring(pathIndex);
}
}
}
return (type, host, port, path);
}
private async Task<int> GenExperimental(SingboxConfig singboxConfig)
{
//if (_config.guiItem.enableStatistics)

View file

@ -54,12 +54,12 @@ public class CoreConfigV2rayService
await GenInbounds(v2rayConfig);
await GenRouting(v2rayConfig);
await GenOutbound(node, v2rayConfig.outbounds.First());
await GenMoreOutbounds(node, v2rayConfig);
await GenRouting(v2rayConfig);
await GenDns(node, v2rayConfig);
await GenStatistic(v2rayConfig);
@ -120,7 +120,7 @@ public class CoreConfigV2rayService
{
continue;
}
if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC)
if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)
{
continue;
}
@ -556,6 +556,8 @@ public class CoreConfigV2rayService
{
return 0;
}
rule.outboundTag = await GenRoutingUserRuleOutbound(rule.outboundTag, v2rayConfig);
if (rule.port.IsNullOrEmpty())
{
rule.port = null;
@ -627,6 +629,32 @@ public class CoreConfigV2rayService
return await Task.FromResult(0);
}
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, V2rayConfig v2rayConfig)
{
if (Global.OutboundTags.Contains(outboundTag))
{
return outboundTag;
}
var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag);
if (node == null
|| node.ConfigType == EConfigType.Custom
|| node.ConfigType == EConfigType.Hysteria2
|| node.ConfigType == EConfigType.TUIC
|| node.ConfigType == EConfigType.Anytls)
{
return Global.ProxyTag;
}
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(node, outbound);
outbound.tag = Global.ProxyTag + node.IndexId.ToString();
v2rayConfig.outbounds.Add(outbound);
return outbound.tag;
}
private async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound)
{
try
@ -659,7 +687,7 @@ public class CoreConfigV2rayService
{
usersItem = vnextItem.users.First();
}
usersItem.id = node.Id;
usersItem.alterId = node.AlterId;
usersItem.email = Global.UserEMail;
@ -1194,6 +1222,7 @@ public class CoreConfigV2rayService
&& prevNode.ConfigType != EConfigType.Custom
&& prevNode.ConfigType != EConfigType.Hysteria2
&& prevNode.ConfigType != EConfigType.TUIC
&& prevNode.ConfigType != EConfigType.Anytls
&& Utils.IsDomain(prevNode.Address))
{
domainList.Add(prevNode.Address);
@ -1205,6 +1234,7 @@ public class CoreConfigV2rayService
&& nextNode.ConfigType != EConfigType.Custom
&& nextNode.ConfigType != EConfigType.Hysteria2
&& nextNode.ConfigType != EConfigType.TUIC
&& nextNode.ConfigType != EConfigType.Anytls
&& Utils.IsDomain(nextNode.Address))
{
domainList.Add(nextNode.Address);
@ -1318,11 +1348,12 @@ public class CoreConfigV2rayService
//Previous proxy
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
string? prevOutboundTag = null;
string? prevOutboundTag = null;
if (prevNode is not null
&& prevNode.ConfigType != EConfigType.Custom
&& prevNode.ConfigType != EConfigType.Hysteria2
&& prevNode.ConfigType != EConfigType.TUIC)
&& prevNode.ConfigType != EConfigType.TUIC
&& prevNode.ConfigType != EConfigType.Anytls)
{
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(prevNode, prevOutbound);
@ -1397,7 +1428,8 @@ public class CoreConfigV2rayService
if (prevNode is not null
&& prevNode.ConfigType != EConfigType.Custom
&& prevNode.ConfigType != EConfigType.Hysteria2
&& prevNode.ConfigType != EConfigType.TUIC)
&& prevNode.ConfigType != EConfigType.TUIC
&& prevNode.ConfigType != EConfigType.Anytls)
{
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(prevNode, prevOutbound);
@ -1466,7 +1498,8 @@ public class CoreConfigV2rayService
if (nextNode is not null
&& nextNode.ConfigType != EConfigType.Custom
&& nextNode.ConfigType != EConfigType.Hysteria2
&& nextNode.ConfigType != EConfigType.TUIC)
&& nextNode.ConfigType != EConfigType.TUIC
&& nextNode.ConfigType != EConfigType.Anytls)
{
if (nextOutbound == null)
{

View file

@ -358,8 +358,8 @@ public class SpeedtestService
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
{
List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC).ToList();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{

View file

@ -536,6 +536,11 @@ public class UpdateService
}
}
//append dns items TODO
geoSiteFiles.Add("cn");
geoSiteFiles.Add("geolocation-cn");
geoSiteFiles.Add("category-ads-all");
var path = Utils.GetBinPath("srss");
if (!Directory.Exists(path))
{

View file

@ -20,6 +20,7 @@ public class MainWindowViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> AddHysteria2ServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddAnytlsServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
@ -111,6 +112,10 @@ public class MainWindowViewModel : MyReactiveObject
{
await AddServerAsync(true, EConfigType.WireGuard);
});
AddAnytlsServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerAsync(true, EConfigType.Anytls);
});
AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerAsync(true, EConfigType.Custom);

View file

@ -436,30 +436,34 @@ public class StatusBarViewModel : MyReactiveObject
private async Task DoEnableTun(bool c)
{
if (_config.TunModeItem.EnableTun != EnableTun)
if (_config.TunModeItem.EnableTun == EnableTun)
{
return;
}
_config.TunModeItem.EnableTun = EnableTun;
if (EnableTun && AllowEnableTun() == false)
{
_config.TunModeItem.EnableTun = EnableTun;
// When running as a non-administrator, reboot to administrator mode
if (EnableTun && AllowEnableTun() == false)
if (Utils.IsWindows())
{
if (Utils.IsWindows())
_config.TunModeItem.EnableTun = false;
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
return;
}
else
{
bool? passwordResult = await _updateView?.Invoke(EViewAction.PasswordInput, null);
if (passwordResult == false)
{
_config.TunModeItem.EnableTun = false;
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
return;
}
else
{
if (await _updateView?.Invoke(EViewAction.PasswordInput, null) == false)
{
_config.TunModeItem.EnableTun = false;
return;
}
}
}
await ConfigHandler.SaveConfig(_config);
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
}
await ConfigHandler.SaveConfig(_config);
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
}
private bool AllowEnableTun()

View file

@ -61,11 +61,7 @@ internal class Program
//.WithInterFont()
.WithFontByDefault()
.LogToTrace()
#if OS_OSX
.UseReactiveUI()
.With(new MacOSPlatformOptions { ShowInDock = AppHandler.Instance.Config.UiItem.MacOSShowInDock });
#else
.UseReactiveUI();
#endif
}
}

View file

@ -21,13 +21,7 @@ public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
btnCancel.Click += (s, e) => this.Close();
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
foreach (ECoreType it in Enum.GetValues(typeof(ECoreType)))
{
if (it == ECoreType.v2rayN)
continue;
cmbCoreType.Items.Add(it.ToString());
}
cmbCoreType.Items.Add(string.Empty);
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
this.WhenActivated(disposables =>
{

View file

@ -533,6 +533,26 @@
HorizontalAlignment="Left"
Watermark="1500" />
</Grid>
<Grid
x:Name="gridAnytls"
Grid.Row="2"
ColumnDefinitions="180,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto,Auto">
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtId10"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
<Separator
x:Name="sepa2"

View file

@ -26,41 +26,22 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
Global.CoreTypes.ForEach(it =>
{
cmbCoreType.Items.Add(it);
});
cmbCoreType.Items.Add(string.Empty);
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
cmbNetwork.ItemsSource = Global.Networks;
cmbFingerprint.ItemsSource = Global.Fingerprints;
cmbFingerprint2.ItemsSource = Global.Fingerprints;
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
cmbAlpn.ItemsSource = Global.Alpns;
cmbStreamSecurity.Items.Add(string.Empty);
cmbStreamSecurity.Items.Add(Global.StreamSecurity);
Global.Networks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbFingerprint.Items.Add(it);
cmbFingerprint2.Items.Add(it);
});
Global.AllowInsecure.ForEach(it =>
{
cmbAllowInsecure.Items.Add(it);
});
Global.Alpns.ForEach(it =>
{
cmbAlpn.Items.Add(it);
});
var lstStreamSecurity = new List<string>();
lstStreamSecurity.Add(string.Empty);
lstStreamSecurity.Add(Global.StreamSecurity);
switch (profileItem.ConfigType)
{
case EConfigType.VMess:
gridVMess.IsVisible = true;
Global.VmessSecurities.ForEach(it =>
{
cmbSecurity.Items.Add(it);
});
cmbSecurity.ItemsSource = Global.VmessSecurities;
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.DefaultSecurity;
@ -69,10 +50,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.Shadowsocks:
gridSs.IsVisible = true;
AppHandler.Instance.GetShadowsocksSecurities(profileItem).ForEach(it =>
{
cmbSecurity3.Items.Add(it);
});
cmbSecurity3.ItemsSource = AppHandler.Instance.GetShadowsocksSecurities(profileItem);
break;
case EConfigType.SOCKS:
@ -82,11 +60,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.VLESS:
gridVLESS.IsVisible = true;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow5.Items.Add(it);
});
lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbFlow5.ItemsSource = Global.Flows;
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.None;
@ -95,11 +70,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.Trojan:
gridTrojan.IsVisible = true;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow6.Items.Add(it);
});
lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbFlow6.ItemsSource = Global.Flows;
break;
case EConfigType.Hysteria2:
@ -119,10 +91,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
Global.TuicCongestionControls.ForEach(it =>
{
cmbHeaderType8.Items.Add(it);
});
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
break;
case EConfigType.WireGuard:
@ -133,7 +102,14 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
gridTls.IsVisible = false;
break;
case EConfigType.Anytls:
gridAnytls.IsVisible = true;
lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbCoreType.IsEnabled = false;
break;
}
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
gridTlsMore.IsVisible = false;
@ -197,6 +173,10 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
break;
case EConfigType.Anytls:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables);
@ -272,44 +252,41 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
private void SetHeaderType()
{
cmbHeaderType.Items.Clear();
var lstHeaderType = new List<string>();
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
cmbHeaderType.Items.Add(Global.None);
lstHeaderType.Add(Global.None);
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
return;
}
if (network == nameof(ETransport.tcp))
{
cmbHeaderType.Items.Add(Global.None);
cmbHeaderType.Items.Add(Global.TcpHeaderHttp);
lstHeaderType.Add(Global.None);
lstHeaderType.Add(Global.TcpHeaderHttp);
}
else if (network is nameof(ETransport.kcp) or nameof(ETransport.quic))
{
cmbHeaderType.Items.Add(Global.None);
Global.KcpHeaderTypes.ForEach(it =>
{
cmbHeaderType.Items.Add(it);
});
lstHeaderType.Add(Global.None);
lstHeaderType.AddRange(Global.KcpHeaderTypes);
}
else if (network is nameof(ETransport.xhttp))
{
Global.XhttpMode.ForEach(it =>
{
cmbHeaderType.Items.Add(it);
});
lstHeaderType.AddRange(Global.XhttpMode);
}
else if (network == nameof(ETransport.grpc))
{
cmbHeaderType.Items.Add(Global.GrpcGunMode);
cmbHeaderType.Items.Add(Global.GrpcMultiMode);
lstHeaderType.Add(Global.GrpcGunMode);
lstHeaderType.Add(Global.GrpcMultiMode);
}
else
{
cmbHeaderType.Items.Add(Global.None);
lstHeaderType.Add(Global.None);
}
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
}

View file

@ -17,22 +17,10 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
btnCancel.Click += (s, e) => this.Close();
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
Global.DomainStrategy4Freedoms.ForEach(it =>
{
cmbdomainStrategy4Freedom.Items.Add(it);
});
Global.SingboxDomainStrategy4Out.ForEach(it =>
{
cmbdomainStrategy4Out.Items.Add(it);
});
Global.DomainDNSAddress.ForEach(it =>
{
cmbdomainDNSAddress.Items.Add(it);
});
Global.SingboxDomainDNSAddress.ForEach(it =>
{
cmbdomainDNSAddress2.Items.Add(it);
});
cmbdomainStrategy4Freedom.ItemsSource = Global.DomainStrategy4Freedoms;
cmbdomainStrategy4Out.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbdomainDNSAddress.ItemsSource = Global.DomainDNSAddress;
cmbdomainDNSAddress2.ItemsSource = Global.SingboxDomainDNSAddress;
this.WhenActivated(disposables =>
{

View file

@ -46,6 +46,7 @@
<Separator />
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
</MenuItem>
<MenuItem Padding="8,0">

View file

@ -83,6 +83,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
@ -134,7 +135,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
break;
}
});
if (Utils.IsWindows())
{

View file

@ -19,87 +19,39 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
Global.destOverrideProtocols.ForEach(it =>
{
clbdestOverride.Items.Add(it);
});
clbdestOverride.ItemsSource = Global.destOverrideProtocols;
_config.Inbound.First().DestOverride?.ForEach(it =>
{
clbdestOverride.SelectedItems.Add(it);
});
Global.IEProxyProtocols.ForEach(it =>
{
cmbsystemProxyAdvancedProtocol.Items.Add(it);
});
Global.LogLevels.ForEach(it =>
{
cmbloglevel.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbdefFingerprint.Items.Add(it);
});
Global.UserAgent.ForEach(it =>
{
cmbdefUserAgent.Items.Add(it);
});
Global.SingboxMuxs.ForEach(it =>
{
cmbmux4SboxProtocol.Items.Add(it);
});
Global.TunMtus.ForEach(it =>
{
cmbMtu.Items.Add(it);
});
Global.TunStacks.ForEach(it =>
{
cmbStack.Items.Add(it);
});
Global.CoreTypes.ForEach(it =>
{
cmbCoreType1.Items.Add(it);
cmbCoreType2.Items.Add(it);
cmbCoreType3.Items.Add(it);
cmbCoreType4.Items.Add(it);
cmbCoreType5.Items.Add(it);
cmbCoreType6.Items.Add(it);
cmbCoreType9.Items.Add(it);
});
cmbsystemProxyAdvancedProtocol.ItemsSource = Global.IEProxyProtocols;
cmbloglevel.ItemsSource = Global.LogLevels;
cmbdefFingerprint.ItemsSource = Global.Fingerprints;
cmbdefUserAgent.ItemsSource = Global.UserAgent;
cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs;
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
for (var i = 2; i <= 8; i++)
{
cmbMixedConcurrencyCount.Items.Add(i);
}
for (var i = 2; i <= 6; i++)
{
cmbSpeedTestTimeout.Items.Add(i * 5);
}
cmbCoreType1.ItemsSource = Global.CoreTypes;
cmbCoreType2.ItemsSource = Global.CoreTypes;
cmbCoreType3.ItemsSource = Global.CoreTypes;
cmbCoreType4.ItemsSource = Global.CoreTypes;
cmbCoreType5.ItemsSource = Global.CoreTypes;
cmbCoreType6.ItemsSource = Global.CoreTypes;
cmbCoreType9.ItemsSource = Global.CoreTypes;
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList();
cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
cmbSpeedPingTestUrl.ItemsSource = Global.SpeedPingTestUrls;
cmbSubConvertUrl.ItemsSource = Global.SubConvertUrls;
cmbGetFilesSourceUrl.ItemsSource = Global.GeoFilesSources;
cmbSrsFilesSourceUrl.ItemsSource = Global.SingboxRulesetSources;
cmbRoutingRulesSourceUrl.ItemsSource = Global.RoutingRulesSources;
cmbIPAPIUrl.ItemsSource = Global.IPAPIUrls;
Global.GeoFilesSources.ForEach(it =>
{
cmbGetFilesSourceUrl.Items.Add(it);
});
Global.SingboxRulesetSources.ForEach(it =>
{
cmbSrsFilesSourceUrl.Items.Add(it);
});
Global.RoutingRulesSources.ForEach(it =>
{
cmbRoutingRulesSourceUrl.Items.Add(it);
});
Global.IPAPIUrls.ForEach(it =>
{
cmbIPAPIUrl.Items.Add(it);
});
foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation)))
{
cmbMainGirdOrientation.Items.Add(it.ToString());
}
cmbMainGirdOrientation.ItemsSource = Utils.GetEnumNames<EGirdOrientation>();
this.WhenActivated(disposables =>
{

View file

@ -2,6 +2,7 @@
x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@ -22,86 +23,95 @@
<TextBlock
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Center"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.LvRemarks}" />
<TextBox
x:Name="txtRemarks"
Grid.Row="0"
Grid.Column="1"
Width="200"
HorizontalAlignment="Left"
Margin="{StaticResource Margin4}" />
Width="300"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<ToggleSwitch
x:Name="togEnabled"
Grid.Row="0"
Grid.Column="2"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
Margin="{StaticResource Margin4}" />
VerticalAlignment="Center" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="outboundTag" />
<ComboBox
x:Name="cmbOutboundTag"
<ctrls:AutoCompleteBox
Name="cmbOutboundTag"
Grid.Row="1"
Grid.Column="1"
Width="200"
Width="300"
Margin="{StaticResource Margin4}"
MaxDropDownHeight="1000" />
Text="{Binding SelectedSource.OutboundTag, Mode=TwoWay}" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
VerticalAlignment="Center"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="port" />
<TextBox
x:Name="txtPort"
Grid.Row="2"
Grid.Column="1"
Width="200"
HorizontalAlignment="Left"
Margin="{StaticResource Margin4}" />
Width="300"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="2"
Grid.Column="2"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="{StaticResource Margin4}">
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="protocol" />
<ListBox
x:Name="clbProtocol"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
SelectionMode="Multiple,Toggle"
Theme="{DynamicResource CardCheckGroupListBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center">
<HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click">
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
</HyperlinkButton>
</TextBlock>
<TextBlock
Grid.Row="3"
Grid.Column="0"
VerticalAlignment="Center"
Margin="{StaticResource Margin4}"
Text="protocol" />
<ListBox
x:Name="clbProtocol"
Grid.Row="3"
Grid.Column="1"
HorizontalAlignment="Left"
Margin="{StaticResource Margin4}"
SelectionMode="Multiple,Toggle"
Theme="{DynamicResource CardCheckGroupListBox}" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
VerticalAlignment="Center"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="inboundTag" />
<ListBox
x:Name="clbInboundTag"
@ -113,35 +123,36 @@
<TextBlock
Grid.Row="4"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
VerticalAlignment="Center"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="network" />
<ComboBox
x:Name="cmbNetwork"
Grid.Row="5"
Grid.Column="1"
Width="200"
Width="300"
Margin="{StaticResource Margin4}"
MaxDropDownHeight="1000" />
<TextBlock
Grid.Row="5"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRoutingTips}" />
</Grid>
<StackPanel
HorizontalAlignment="Right"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<StackPanel

View file

@ -23,21 +23,11 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
ViewModel = new RoutingRuleDetailsViewModel(rulesItem, UpdateViewHandler);
cmbOutboundTag.Items.Add(Global.ProxyTag);
cmbOutboundTag.Items.Add(Global.DirectTag);
cmbOutboundTag.Items.Add(Global.BlockTag);
Global.RuleProtocols.ForEach(it =>
{
clbProtocol.Items.Add(it);
});
Global.InboundTags.ForEach(it =>
{
clbInboundTag.Items.Add(it);
});
Global.RuleNetworks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
cmbOutboundTag.ItemsSource = Global.OutboundTags;
clbProtocol.ItemsSource = Global.RuleProtocols;
clbInboundTag.ItemsSource = Global.InboundTags;
cmbNetwork.ItemsSource = Global.RuleNetworks;
if (!rulesItem.Id.IsNullOrEmpty())
{
@ -54,7 +44,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables);
@ -80,7 +70,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
cmbOutboundTag.Focus();
txtRemarks.Focus();
}
private void ClbProtocol_SelectionChanged(object? sender, SelectionChangedEventArgs e)

View file

@ -30,15 +30,9 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
btnBrowseCustomRulesetPath4Singbox.Click += btnBrowseCustomRulesetPath4Singbox_ClickAsync;
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
{
cmbdomainStrategy.Items.Add(it);
});
cmbdomainStrategy.Items.Add(string.Empty);
Global.DomainStrategies4Singbox.ForEach(it =>
{
cmbdomainStrategy4Singbox.Items.Add(it);
});
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
this.WhenActivated(disposables =>
{

View file

@ -26,18 +26,9 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
{
cmbdomainStrategy.Items.Add(it);
});
Global.DomainMatchers.ForEach(it =>
{
cmbdomainMatcher.Items.Add(it);
});
Global.DomainStrategies4Singbox.ForEach(it =>
{
cmbdomainStrategy4Singbox.Items.Add(it);
});
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
cmbdomainMatcher.ItemsSource = Global.DomainMatchers;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
this.WhenActivated(disposables =>
{
@ -126,7 +117,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy");
}
private void btnCancel_Click(object? sender, RoutedEventArgs e)

View file

@ -104,13 +104,15 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
{
var dialog = new SudoPasswordInputView();
var obj = await DialogHost.Show(dialog);
if (obj == null)
var password = obj?.ToString();
if (password.IsNullOrEmpty())
{
togEnableTun.IsChecked = false;
return false;
}
AppHandler.Instance.LinuxSudoPwd = obj.ToString() ?? string.Empty;
AppHandler.Instance.LinuxSudoPwd = password;
return true;
}

View file

@ -22,10 +22,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
Global.SubConvertTargets.ForEach(it =>
{
cmbConvertTarget.Items.Add(it);
});
cmbConvertTarget.ItemsSource = Global.SubConvertTargets;
this.WhenActivated(disposables =>
{

View file

@ -1,5 +1,6 @@
using Avalonia.Controls;
using Avalonia.Threading;
using CliWrap.Buffered;
using DialogHostAvalonia;
namespace v2rayN.Desktop.Views;
@ -12,22 +13,70 @@ public partial class SudoPasswordInputView : UserControl
this.Loaded += (s, e) => txtPassword.Focus();
btnSave.Click += (_, _) =>
{
if (string.IsNullOrEmpty(txtPassword.Text))
{
txtPassword.Focus();
return;
}
Dispatcher.UIThread.Post(() =>
{
DialogHost.Close(null, txtPassword.Text);
});
};
btnSave.Click += async (_, _) => await SavePasswordAsync();
btnCancel.Click += (_, _) =>
{
DialogHost.Close(null);
};
}
private async Task SavePasswordAsync()
{
if (txtPassword.Text.IsNullOrEmpty())
{
txtPassword.Focus();
return;
}
var password = txtPassword.Text;
btnSave.IsEnabled = false;
try
{
// Verify if the password is correct
if (await CheckSudoPasswordAsync(password))
{
// Password verification successful, return password and close dialog
await Dispatcher.UIThread.InvokeAsync(() =>
{
DialogHost.Close(null, password);
});
}
else
{
// Password verification failed, display error and let user try again
NoticeHandler.Instance.Enqueue(ResUI.SudoIncorrectPasswordTip);
txtPassword.Focus();
}
}
catch (Exception ex)
{
Logging.SaveLog("SudoPassword", ex);
}
finally
{
btnSave.IsEnabled = true;
}
}
private async Task<bool> CheckSudoPasswordAsync(string password)
{
try
{
// 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)
.WithArguments(arg)
.WithStandardInputPipe(CliWrap.PipeSource.FromString(password))
.ExecuteBufferedAsync();
return result.ExitCode == 0;
}
catch (Exception ex)
{
Logging.SaveLog("CheckSudoPassword", ex);
return false;
}
}
}

View file

@ -16,20 +16,9 @@ public partial class ThemeSettingView : ReactiveUserControl<ThemeSettingViewMode
InitializeComponent();
ViewModel = new ThemeSettingViewModel();
foreach (ETheme it in Enum.GetValues(typeof(ETheme)))
{
cmbCurrentTheme.Items.Add(it.ToString());
}
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 10; i++)
{
cmbCurrentFontSize.Items.Add(i);
}
Global.Languages.ForEach(it =>
{
cmbCurrentLanguage.Items.Add(it);
});
cmbCurrentTheme.ItemsSource = Utils.GetEnumNames<ETheme>();
cmbCurrentFontSize.ItemsSource = Enumerable.Range(Global.MinFontSize, 11).ToList();
cmbCurrentLanguage.ItemsSource = Global.Languages;
this.WhenActivated(disposables =>
{

View file

@ -14,13 +14,7 @@ public partial class AddServer2Window
this.Loaded += Window_Loaded;
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
foreach (ECoreType it in Enum.GetValues(typeof(ECoreType)))
{
if (it == ECoreType.v2rayN)
continue;
cmbCoreType.Items.Add(it.ToString());
}
cmbCoreType.Items.Add(string.Empty);
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
this.WhenActivated(disposables =>
{

View file

@ -707,6 +707,35 @@
materialDesign:HintAssist.Hint="1500"
Style="{StaticResource DefTextBox}" />
</Grid>
<Grid
x:Name="gridAnytls"
Grid.Row="2"
Visibility="Hidden">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtId10"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
<Separator
x:Name="sepa2"

View file

@ -20,41 +20,22 @@ public partial class AddServerWindow
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
Global.CoreTypes.ForEach(it =>
{
cmbCoreType.Items.Add(it);
});
cmbCoreType.Items.Add(string.Empty);
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
cmbNetwork.ItemsSource = Global.Networks;
cmbFingerprint.ItemsSource = Global.Fingerprints;
cmbFingerprint2.ItemsSource = Global.Fingerprints;
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
cmbAlpn.ItemsSource = Global.Alpns;
cmbStreamSecurity.Items.Add(string.Empty);
cmbStreamSecurity.Items.Add(Global.StreamSecurity);
Global.Networks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbFingerprint.Items.Add(it);
cmbFingerprint2.Items.Add(it);
});
Global.AllowInsecure.ForEach(it =>
{
cmbAllowInsecure.Items.Add(it);
});
Global.Alpns.ForEach(it =>
{
cmbAlpn.Items.Add(it);
});
var lstStreamSecurity = new List<string>();
lstStreamSecurity.Add(string.Empty);
lstStreamSecurity.Add(Global.StreamSecurity);
switch (profileItem.ConfigType)
{
case EConfigType.VMess:
gridVMess.Visibility = Visibility.Visible;
Global.VmessSecurities.ForEach(it =>
{
cmbSecurity.Items.Add(it);
});
cmbSecurity.ItemsSource = Global.VmessSecurities;
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.DefaultSecurity;
@ -63,10 +44,7 @@ public partial class AddServerWindow
case EConfigType.Shadowsocks:
gridSs.Visibility = Visibility.Visible;
AppHandler.Instance.GetShadowsocksSecurities(profileItem).ForEach(it =>
{
cmbSecurity3.Items.Add(it);
});
cmbSecurity3.ItemsSource = AppHandler.Instance.GetShadowsocksSecurities(profileItem);
break;
case EConfigType.SOCKS:
@ -76,11 +54,8 @@ public partial class AddServerWindow
case EConfigType.VLESS:
gridVLESS.Visibility = Visibility.Visible;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow5.Items.Add(it);
});
lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbFlow5.ItemsSource = Global.Flows;
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.None;
@ -89,11 +64,8 @@ public partial class AddServerWindow
case EConfigType.Trojan:
gridTrojan.Visibility = Visibility.Visible;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow6.Items.Add(it);
});
lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbFlow6.ItemsSource = Global.Flows;
break;
case EConfigType.Hysteria2:
@ -113,10 +85,7 @@ public partial class AddServerWindow
cmbFingerprint.IsEnabled = false;
cmbFingerprint.Text = string.Empty;
Global.TuicCongestionControls.ForEach(it =>
{
cmbHeaderType8.Items.Add(it);
});
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
break;
case EConfigType.WireGuard:
@ -127,7 +96,14 @@ public partial class AddServerWindow
gridTls.Visibility = Visibility.Collapsed;
break;
case EConfigType.Anytls:
gridAnytls.Visibility = Visibility.Visible;
cmbCoreType.IsEnabled = false;
lstStreamSecurity.Add(Global.StreamSecurityReality);
break;
}
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
gridTlsMore.Visibility = Visibility.Hidden;
@ -191,6 +167,10 @@ public partial class AddServerWindow
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
break;
case EConfigType.Anytls:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables);
@ -267,44 +247,41 @@ public partial class AddServerWindow
private void SetHeaderType()
{
cmbHeaderType.Items.Clear();
var lstHeaderType = new List<string>();
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
cmbHeaderType.Items.Add(Global.None);
lstHeaderType.Add(Global.None);
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
return;
}
if (network == nameof(ETransport.tcp))
{
cmbHeaderType.Items.Add(Global.None);
cmbHeaderType.Items.Add(Global.TcpHeaderHttp);
lstHeaderType.Add(Global.None);
lstHeaderType.Add(Global.TcpHeaderHttp);
}
else if (network is nameof(ETransport.kcp) or nameof(ETransport.quic))
{
cmbHeaderType.Items.Add(Global.None);
Global.KcpHeaderTypes.ForEach(it =>
{
cmbHeaderType.Items.Add(it);
});
lstHeaderType.Add(Global.None);
lstHeaderType.AddRange(Global.KcpHeaderTypes);
}
else if (network is nameof(ETransport.xhttp))
{
Global.XhttpMode.ForEach(it =>
{
cmbHeaderType.Items.Add(it);
});
lstHeaderType.AddRange(Global.XhttpMode);
}
else if (network == nameof(ETransport.grpc))
{
cmbHeaderType.Items.Add(Global.GrpcGunMode);
cmbHeaderType.Items.Add(Global.GrpcMultiMode);
lstHeaderType.Add(Global.GrpcGunMode);
lstHeaderType.Add(Global.GrpcMultiMode);
}
else
{
cmbHeaderType.Items.Add(Global.None);
lstHeaderType.Add(Global.None);
}
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
}

View file

@ -17,22 +17,10 @@ public partial class DNSSettingWindow
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
Global.DomainStrategy4Freedoms.ForEach(it =>
{
cmbdomainStrategy4Freedom.Items.Add(it);
});
Global.SingboxDomainStrategy4Out.ForEach(it =>
{
cmbdomainStrategy4Out.Items.Add(it);
});
Global.DomainDNSAddress.ForEach(it =>
{
cmbdomainDNSAddress.Items.Add(it);
});
Global.SingboxDomainDNSAddress.ForEach(it =>
{
cmbdomainDNSAddress2.Items.Add(it);
});
cmbdomainStrategy4Freedom.ItemsSource = Global.DomainStrategy4Freedoms;
cmbdomainStrategy4Out.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbdomainDNSAddress.ItemsSource = Global.DomainDNSAddress;
cmbdomainDNSAddress2.ItemsSource = Global.SingboxDomainDNSAddress;
this.WhenActivated(disposables =>
{

View file

@ -108,6 +108,10 @@
x:Name="menuAddTuicServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddTuicServer}" />
<MenuItem
x:Name="menuAddAnytlsServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
</MenuItem>
</Menu>
<Separator />

View file

@ -80,6 +80,7 @@ public partial class MainWindow
this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);

View file

@ -25,11 +25,8 @@ public partial class MsgView
menuMsgViewCopy.Click += menuMsgViewCopy_Click;
menuMsgViewCopyAll.Click += menuMsgViewCopyAll_Click;
menuMsgViewClear.Click += menuMsgViewClear_Click;
Global.PresetMsgFilters.ForEach(it =>
{
cmbMsgFilter.Items.Add(it);
});
cmbMsgFilter.ItemsSource = Global.PresetMsgFilters;
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)

View file

@ -21,94 +21,39 @@ public partial class OptionSettingWindow
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
Global.destOverrideProtocols.ForEach(it =>
{
clbdestOverride.Items.Add(it);
});
clbdestOverride.ItemsSource = Global.destOverrideProtocols;
_config.Inbound.First().DestOverride?.ForEach(it =>
{
clbdestOverride.SelectedItems.Add(it);
});
Global.IEProxyProtocols.ForEach(it =>
{
cmbsystemProxyAdvancedProtocol.Items.Add(it);
});
Global.LogLevels.ForEach(it =>
{
cmbloglevel.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbdefFingerprint.Items.Add(it);
});
Global.UserAgent.ForEach(it =>
{
cmbdefUserAgent.Items.Add(it);
});
Global.SingboxMuxs.ForEach(it =>
{
cmbmux4SboxProtocol.Items.Add(it);
});
Global.TunMtus.ForEach(it =>
{
cmbMtu.Items.Add(it);
});
Global.TunStacks.ForEach(it =>
{
cmbStack.Items.Add(it);
});
Global.CoreTypes.ForEach(it =>
{
cmbCoreType1.Items.Add(it);
cmbCoreType2.Items.Add(it);
cmbCoreType3.Items.Add(it);
cmbCoreType4.Items.Add(it);
cmbCoreType5.Items.Add(it);
cmbCoreType6.Items.Add(it);
cmbCoreType9.Items.Add(it);
});
cmbsystemProxyAdvancedProtocol.ItemsSource = Global.IEProxyProtocols;
cmbloglevel.ItemsSource = Global.LogLevels;
cmbdefFingerprint.ItemsSource = Global.Fingerprints;
cmbdefUserAgent.ItemsSource = Global.UserAgent;
cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs;
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
for (var i = 2; i <= 8; i++)
{
cmbMixedConcurrencyCount.Items.Add(i);
}
for (var i = 2; i <= 6; i++)
{
cmbSpeedTestTimeout.Items.Add(i * 5);
}
Global.SpeedTestUrls.ForEach(it =>
{
cmbSpeedTestUrl.Items.Add(it);
});
Global.SpeedPingTestUrls.ForEach(it =>
{
cmbSpeedPingTestUrl.Items.Add(it);
});
Global.SubConvertUrls.ForEach(it =>
{
cmbSubConvertUrl.Items.Add(it);
});
Global.GeoFilesSources.ForEach(it =>
{
cmbGetFilesSourceUrl.Items.Add(it);
});
Global.SingboxRulesetSources.ForEach(it =>
{
cmbSrsFilesSourceUrl.Items.Add(it);
});
Global.RoutingRulesSources.ForEach(it =>
{
cmbRoutingRulesSourceUrl.Items.Add(it);
});
Global.IPAPIUrls.ForEach(it =>
{
cmbIPAPIUrl.Items.Add(it);
});
foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation)))
{
cmbMainGirdOrientation.Items.Add(it.ToString());
}
cmbCoreType1.ItemsSource = Global.CoreTypes;
cmbCoreType2.ItemsSource = Global.CoreTypes;
cmbCoreType3.ItemsSource = Global.CoreTypes;
cmbCoreType4.ItemsSource = Global.CoreTypes;
cmbCoreType5.ItemsSource = Global.CoreTypes;
cmbCoreType6.ItemsSource = Global.CoreTypes;
cmbCoreType9.ItemsSource = Global.CoreTypes;
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList();
cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
cmbSpeedPingTestUrl.ItemsSource = Global.SpeedPingTestUrls;
cmbSubConvertUrl.ItemsSource = Global.SubConvertUrls;
cmbGetFilesSourceUrl.ItemsSource = Global.GeoFilesSources;
cmbSrsFilesSourceUrl.ItemsSource = Global.SingboxRulesetSources;
cmbRoutingRulesSourceUrl.ItemsSource = Global.RoutingRulesSources;
cmbIPAPIUrl.ItemsSource = Global.IPAPIUrls;
cmbMainGirdOrientation.ItemsSource = Utils.GetEnumNames<EGirdOrientation>();
this.WhenActivated(disposables =>
{
@ -209,8 +154,7 @@ public partial class OptionSettingWindow
private async Task InitSettingFont()
{
var lstFonts = await GetFonts(Utils.GetFontsPath());
lstFonts.ForEach(it => { cmbcurrentFontFamily.Items.Add(it); });
cmbcurrentFontFamily.Items.Add(string.Empty);
cmbcurrentFontFamily.ItemsSource = lstFonts.AppendEmpty();
}
private async Task<List<string>> GetFonts(string path)

View file

@ -53,7 +53,8 @@
Grid.Row="0"
Grid.Column="2"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="1"
@ -68,6 +69,7 @@
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
IsEditable="True"
MaxDropDownHeight="1000"
Style="{StaticResource DefComboBox}" />
<TextBlock
@ -75,8 +77,9 @@
Grid.Column="2"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
<TextBlock
Grid.Row="2"
@ -97,13 +100,10 @@
Grid.Row="2"
Grid.Column="2"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}">
<Hyperlink Click="linkRuleobjectDoc_Click">
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
<materialDesign:PackIcon Kind="Link" />
</Hyperlink>
</TextBlock>
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
<TextBlock
Grid.Row="3"
@ -120,6 +120,17 @@
HorizontalAlignment="Left"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilterChipPrimaryListBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}">
<Hyperlink Click="linkRuleobjectDoc_Click">
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
<materialDesign:PackIcon Kind="Link" />
</Hyperlink>
</TextBlock>
<TextBlock
Grid.Row="4"
@ -140,6 +151,7 @@
Grid.Column="2"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
@ -164,6 +176,7 @@
Grid.Column="2"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRoutingTips}" />
</Grid>

View file

@ -16,21 +16,11 @@ public partial class RoutingRuleDetailsWindow
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
ViewModel = new RoutingRuleDetailsViewModel(rulesItem, UpdateViewHandler);
cmbOutboundTag.Items.Add(Global.ProxyTag);
cmbOutboundTag.Items.Add(Global.DirectTag);
cmbOutboundTag.Items.Add(Global.BlockTag);
Global.RuleProtocols.ForEach(it =>
{
clbProtocol.Items.Add(it);
});
Global.InboundTags.ForEach(it =>
{
clbInboundTag.Items.Add(it);
});
Global.RuleNetworks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
cmbOutboundTag.ItemsSource = Global.OutboundTags;
clbProtocol.ItemsSource = Global.RuleProtocols;
clbInboundTag.ItemsSource = Global.InboundTags;
cmbNetwork.ItemsSource = Global.RuleNetworks;
if (!rulesItem.Id.IsNullOrEmpty())
{
@ -74,7 +64,7 @@ public partial class RoutingRuleDetailsWindow
private void Window_Loaded(object sender, RoutedEventArgs e)
{
cmbOutboundTag.Focus();
txtRemarks.Focus();
}
private void ClbProtocol_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)

View file

@ -21,15 +21,9 @@ public partial class RoutingRuleSettingWindow
btnBrowseCustomRulesetPath4Singbox.Click += btnBrowseCustomRulesetPath4Singbox_Click;
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
{
cmbdomainStrategy.Items.Add(it);
});
cmbdomainStrategy.Items.Add(string.Empty);
Global.DomainStrategies4Singbox.ForEach(it =>
{
cmbdomainStrategy4Singbox.Items.Add(it);
});
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
this.WhenActivated(disposables =>
{

View file

@ -21,18 +21,9 @@ public partial class RoutingSettingWindow
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
{
cmbdomainStrategy.Items.Add(it);
});
Global.DomainMatchers.ForEach(it =>
{
cmbdomainMatcher.Items.Add(it);
});
Global.DomainStrategies4Singbox.ForEach(it =>
{
cmbdomainStrategy4Singbox.Items.Add(it);
});
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
cmbdomainMatcher.ItemsSource = Global.DomainMatchers;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
this.WhenActivated(disposables =>
{
@ -131,7 +122,7 @@ public partial class RoutingSettingWindow
private void linkdomainStrategy4Singbox_Click(object sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy");
}
private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)

View file

@ -15,10 +15,7 @@ public partial class SubEditWindow
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
Global.SubConvertTargets.ForEach(it =>
{
cmbConvertTarget.Items.Add(it);
});
cmbConvertTarget.ItemsSource = Global.SubConvertTargets;
this.WhenActivated(disposables =>
{

View file

@ -13,22 +13,10 @@ public partial class ThemeSettingView
{
InitializeComponent();
ViewModel = new ThemeSettingViewModel();
foreach (ETheme it in Enum.GetValues(typeof(ETheme)))
{
if ((int)it > 2)
continue;
cmbCurrentTheme.Items.Add(it.ToString());
}
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 10; i++)
{
cmbCurrentFontSize.Items.Add(i.ToString());
}
Global.Languages.ForEach(it =>
{
cmbCurrentLanguage.Items.Add(it);
});
cmbCurrentTheme.ItemsSource = Utils.GetEnumNames<ETheme>().Take(3).ToList();
cmbCurrentFontSize.ItemsSource = Enumerable.Range(Global.MinFontSize, 11).ToList();
cmbCurrentLanguage.ItemsSource = Global.Languages;
this.WhenActivated(disposables =>
{