Compare commits

...

15 Commits

Author SHA1 Message Date
2dust
693afe3560 up 7.16.4 2025-11-23 14:33:01 +08:00
2dust
95361e8b65 Simplify configuration-related resource strings 2025-11-23 14:31:29 +08:00
2dust
3ff7299aca Refactor update result handling and model 2025-11-23 14:06:34 +08:00
DHR60
34fc4de0c2 Avoid xray warning (#8369) 2025-11-22 19:17:18 +08:00
DHR60
91536d3923 Fix sing-box ws (#8367)
* Fix sing-box ws

* Parse eh
2025-11-22 16:40:07 +08:00
2dust
6b87c09a96 Add confirmation before removing duplicate servers
https://github.com/2dust/v2rayN/issues/8365
2025-11-22 10:20:16 +08:00
2dust
ecaac2ac61 Fix
https://github.com/2dust/v2rayN/discussions/8366
2025-11-22 10:16:53 +08:00
2dust
ad74b1584d Refactor dialog layouts 2025-11-21 20:30:41 +08:00
dependabot[bot]
514d76953a Bump actions/checkout from 5.0.1 to 6.0.0 (#8359)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.1 to 6.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5.0.1...v6.0.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-21 15:57:37 +08:00
DHR60
5b82f17995 Fix (#8363)
* Fix

* AI-optimized code
2025-11-21 15:56:42 +08:00
2dust
20ce35bc30 Update dotnet publish syntax in build workflows 2025-11-20 20:04:14 +08:00
2dust
c0fca0dddd Update build script and remove global.json 2025-11-20 19:56:14 +08:00
2dust
2ba896e17e Update Directory.Packages.props 2025-11-20 19:39:29 +08:00
DHR60
f61e6d8c63 perf: Shadowsocks (#8352)
* perf: Shadowsocks

* stricter plugin name fix for SIP002 URI

* Fix
2025-11-20 19:35:57 +08:00
2dust
d3e2e55ecf Add global.json for SDK configuration
Introduces a global.json file specifying the .NET SDK version (8.0.416) and disables rollForward to ensure consistent SDK usage across environments.
2025-11-20 10:12:44 +08:00
24 changed files with 481 additions and 267 deletions

View File

@@ -31,7 +31,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
uses: actions/checkout@v6.0.0
with:
submodules: 'recursive'
fetch-depth: '0'
@@ -44,10 +44,10 @@ jobs:
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o "$OutputPath64"
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o "$OutputPathArm64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPath64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 -p:SelfContained=true -o "$OutputPath64"
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 -p:SelfContained=true -o "$OutputPathArm64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 -p:SelfContained=true -p:PublishTrimmed=true -o "$OutputPath64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
- name: Upload build artifacts
uses: actions/upload-artifact@v5.0.0
@@ -110,7 +110,7 @@ jobs:
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
- name: Checkout repo (for scripts)
uses: actions/checkout@v5.0.1
uses: actions/checkout@v6.0.0
with:
submodules: 'recursive'
fetch-depth: '0'

View File

@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
uses: actions/checkout@v6.0.0
with:
submodules: 'recursive'
fetch-depth: '0'
@@ -39,10 +39,10 @@ jobs:
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 -p:SelfContained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 -p:SelfContained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 -p:SelfContained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v5.0.0

View File

@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
uses: actions/checkout@v6.0.0
with:
submodules: 'recursive'
fetch-depth: '0'
@@ -39,10 +39,10 @@ jobs:
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v5.0.0

View File

@@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
uses: actions/checkout@v6.0.0
- name: Setup
uses: actions/setup-dotnet@v5.0.0
@@ -37,12 +37,12 @@ jobs:
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 -p:SelfContained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
- name: Upload build artifacts
@@ -68,4 +68,4 @@ jobs:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true
prerelease: true

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.16.3</Version>
<Version>7.16.4</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -6,11 +6,11 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.8" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.8" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.9" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.9" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.9" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="CliWrap" Version="3.10.0" />
<PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.2" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />

View File

@@ -41,7 +41,66 @@ public class ShadowsocksFmt : BaseFmt
//url = Utile.Base64Encode(url);
//new Sip002
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
// plugin
var plugin = string.Empty;
var pluginArgs = string.Empty;
if (item.Network == nameof(ETransport.tcp) && item.HeaderType == Global.TcpHeaderHttp)
{
plugin = "obfs-local";
pluginArgs = $"obfs=http;obfs-host={item.RequestHost};";
}
else
{
if (item.Network == nameof(ETransport.ws))
{
pluginArgs += "mode=websocket;";
pluginArgs += $"host={item.RequestHost};";
pluginArgs += $"path={item.Path};";
}
else if (item.Network == nameof(ETransport.quic))
{
pluginArgs += "mode=quic;";
}
if (item.StreamSecurity == Global.StreamSecurity)
{
pluginArgs += "tls;";
var certs = CertPemManager.ParsePemChain(item.Cert);
if (certs.Count > 0)
{
var cert = certs.First();
const string beginMarker = "-----BEGIN CERTIFICATE-----\n";
const string endMarker = "\n-----END CERTIFICATE-----";
var base64Content = cert.Replace(beginMarker, "").Replace(endMarker, "").Trim();
// https://github.com/shadowsocks/v2ray-plugin/blob/e9af1cdd2549d528deb20a4ab8d61c5fbe51f306/args.go#L172
// Equal signs and commas [and backslashes] must be escaped with a backslash.
base64Content = base64Content.Replace("=", "\\=");
pluginArgs += $"certRaw={base64Content};";
}
}
if (pluginArgs.Length > 0)
{
plugin = "v2ray-plugin";
}
}
var dicQuery = new Dictionary<string, string>();
if (plugin.IsNotEmpty())
{
var pluginStr = plugin + ";" + pluginArgs;
// pluginStr remove last ';' and url encode
if (pluginStr.EndsWith(';'))
{
pluginStr = pluginStr[..^1];
}
dicQuery["plugin"] = Utils.UrlEncode(pluginStr);
}
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, dicQuery, remark);
}
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
@@ -124,19 +183,81 @@ public class ShadowsocksFmt : BaseFmt
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
if (queryParameters["plugin"] != null)
{
//obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && obfsHost.IsNotEmpty())
{
obfsHost = obfsHost?.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.TcpHeaderHttp;
item.RequestHost = obfsHost ?? "";
}
else
var pluginStr = queryParameters["plugin"];
var pluginParts = pluginStr.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
if (pluginParts.Length == 0)
{
return null;
}
var pluginName = pluginParts[0];
// A typo in https://github.com/shadowsocks/shadowsocks-org/blob/6b1c064db4129de99c516294960e731934841c94/docs/doc/sip002.md?plain=1#L15
// "simple-obfs" should be "obfs-local"
if (pluginName == "simple-obfs")
{
pluginName = "obfs-local";
}
// Parse obfs-local plugin
if (pluginName == "obfs-local")
{
var obfsMode = pluginParts.FirstOrDefault(t => t.StartsWith("obfs="));
var obfsHost = pluginParts.FirstOrDefault(t => t.StartsWith("obfs-host="));
if ((!obfsMode.IsNullOrEmpty()) && obfsMode.Contains("obfs=http") && obfsHost.IsNotEmpty())
{
obfsHost = obfsHost.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.TcpHeaderHttp;
item.RequestHost = obfsHost;
}
}
// Parse v2ray-plugin
else if (pluginName == "v2ray-plugin")
{
var mode = pluginParts.FirstOrDefault(t => t.StartsWith("mode="), "websocket");
var host = pluginParts.FirstOrDefault(t => t.StartsWith("host="));
var path = pluginParts.FirstOrDefault(t => t.StartsWith("path="));
var hasTls = pluginParts.Any(t => t == "tls");
var certRaw = pluginParts.FirstOrDefault(t => t.StartsWith("certRaw="));
var modeValue = mode.Replace("mode=", "");
if (modeValue == "websocket")
{
item.Network = nameof(ETransport.ws);
if (!host.IsNullOrEmpty())
{
item.RequestHost = host.Replace("host=", "");
}
if (!path.IsNullOrEmpty())
{
item.Path = path.Replace("path=", "");
}
}
else if (modeValue == "quic")
{
item.Network = nameof(ETransport.quic);
}
if (hasTls)
{
item.StreamSecurity = Global.StreamSecurity;
if (!certRaw.IsNullOrEmpty())
{
var certBase64 = certRaw.Replace("certRaw=", "");
certBase64 = certBase64.Replace("\\=", "=");
const string beginMarker = "-----BEGIN CERTIFICATE-----\n";
const string endMarker = "\n-----END CERTIFICATE-----";
var certPem = beginMarker + certBase64 + endMarker;
item.Cert = certPem;
}
}
}
}
return item;

View File

@@ -10,6 +10,13 @@ public class ActionPrecheckManager(Config config)
private readonly Config _config = config;
// sing-box supported transports for different protocol types
private static readonly HashSet<string> SingboxUnsupportedTransports = [nameof(ETransport.kcp), nameof(ETransport.xhttp)];
private static readonly HashSet<EConfigType> SingboxTransportSupportedProtocols =
[EConfigType.VMess, EConfigType.VLESS, EConfigType.Trojan, EConfigType.Shadowsocks];
private static readonly HashSet<string> SingboxShadowsocksAllowedTransports =
[nameof(ETransport.tcp), nameof(ETransport.ws), nameof(ETransport.quic)];
public async Task<List<string>> Check(string? indexId)
{
if (indexId.IsNullOrEmpty())
@@ -174,26 +181,16 @@ public class ActionPrecheckManager(Config config)
return errors;
}
var net = item.GetNetwork() ?? item.Network;
var net = item.GetNetwork();
if (coreType == ECoreType.sing_box)
{
// sing-box does not support xhttp / kcp
// sing-box does not support transports like ws/http/httpupgrade/etc. when the node is not vmess/trojan/vless
if (net is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
var transportError = ValidateSingboxTransport(item.ConfigType, net);
if (transportError != null)
{
errors.Add(string.Format(ResUI.CoreNotSupportNetwork, nameof(ECoreType.sing_box), net));
errors.Add(transportError);
return errors;
}
if (item.ConfigType is not (EConfigType.VMess or EConfigType.VLESS or EConfigType.Trojan))
{
if (net is nameof(ETransport.ws) or nameof(ETransport.http) or nameof(ETransport.h2) or nameof(ETransport.quic) or nameof(ETransport.httpupgrade))
{
errors.Add(string.Format(ResUI.CoreNotSupportProtocolTransport, nameof(ECoreType.sing_box), item.ConfigType.ToString(), net));
return errors;
}
}
}
else if (coreType is ECoreType.Xray)
{
@@ -209,6 +206,31 @@ public class ActionPrecheckManager(Config config)
return errors;
}
private static string? ValidateSingboxTransport(EConfigType configType, string net)
{
// sing-box does not support xhttp / kcp transports
if (SingboxUnsupportedTransports.Contains(net))
{
return string.Format(ResUI.CoreNotSupportNetwork, nameof(ECoreType.sing_box), net);
}
// sing-box does not support non-tcp transports for protocols other than vmess/trojan/vless/shadowsocks
if (!SingboxTransportSupportedProtocols.Contains(configType) && net != nameof(ETransport.tcp))
{
return string.Format(ResUI.CoreNotSupportProtocolTransport,
nameof(ECoreType.sing_box), configType.ToString(), net);
}
// sing-box shadowsocks only supports tcp/ws/quic transports
if (configType == EConfigType.Shadowsocks && !SingboxShadowsocksAllowedTransports.Contains(net))
{
return string.Format(ResUI.CoreNotSupportProtocolTransport,
nameof(ECoreType.sing_box), configType.ToString(), net);
}
return null;
}
private async Task<List<string>> ValidateRelatedNodesExistAndValid(ProfileItem? item)
{
var errors = new List<string>();

View File

@@ -216,6 +216,8 @@ public class Transport4Sbox
public string? idle_timeout { get; set; }
public string? ping_timeout { get; set; }
public bool? permit_without_stream { get; set; }
public int? max_early_data { get; set; }
public string? early_data_header_name { get; set; }
}
public class Headers4Sbox

View File

@@ -0,0 +1,21 @@
namespace ServiceLib.Models;
public class UpdateResult
{
public bool Success { get; set; }
public string? Msg { get; set; }
public SemanticVersion? Version { get; set; }
public string? Url { get; set; }
public UpdateResult(bool success, string? msg)
{
Success = success;
Msg = msg;
}
public UpdateResult(bool success, SemanticVersion? version)
{
Success = success;
Version = version;
}
}

View File

@@ -411,8 +411,6 @@ public class WsSettings4Ray
public class Headers4Ray
{
public string Host { get; set; }
[JsonPropertyName("User-Agent")]
public string UserAgent { get; set; }
}

View File

@@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResUI {
@@ -529,7 +529,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Next proxy Configuration remarks 的本地化字符串。
/// 查找类似 Next proxy remarks 的本地化字符串。
/// </summary>
public static string LvNextProfile {
get {
@@ -547,7 +547,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Previous proxy Configuration remarks 的本地化字符串。
/// 查找类似 Previous proxy remarks 的本地化字符串。
/// </summary>
public static string LvPrevProfile {
get {
@@ -736,7 +736,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [Anytls] Configuration 的本地化字符串。
/// 查找类似 Add [Anytls] 的本地化字符串。
/// </summary>
public static string menuAddAnytlsServer {
get {
@@ -745,7 +745,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add Child Configuration 的本地化字符串。
/// 查找类似 Add Child 的本地化字符串。
/// </summary>
public static string menuAddChildServer {
get {
@@ -754,7 +754,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
/// 查找类似 Add a custom configuration 的本地化字符串。
/// </summary>
public static string menuAddCustomServer {
get {
@@ -763,7 +763,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [HTTP] Configuration 的本地化字符串。
/// 查找类似 Add [HTTP] 的本地化字符串。
/// </summary>
public static string menuAddHttpServer {
get {
@@ -772,7 +772,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [Hysteria2] Configuration 的本地化字符串。
/// 查找类似 Add [Hysteria2] 的本地化字符串。
/// </summary>
public static string menuAddHysteria2Server {
get {
@@ -781,7 +781,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add Policy Group Configuration 的本地化字符串。
/// 查找类似 Add Policy Group 的本地化字符串。
/// </summary>
public static string menuAddPolicyGroupServer {
get {
@@ -790,7 +790,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add Proxy Chain Configuration 的本地化字符串。
/// 查找类似 Add Proxy Chain 的本地化字符串。
/// </summary>
public static string menuAddProxyChainServer {
get {
@@ -826,7 +826,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [Shadowsocks] Configuration 的本地化字符串。
/// 查找类似 Add [Shadowsocks] 的本地化字符串。
/// </summary>
public static string menuAddShadowsocksServer {
get {
@@ -835,7 +835,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [SOCKS] Configuration 的本地化字符串。
/// 查找类似 Add [SOCKS] 的本地化字符串。
/// </summary>
public static string menuAddSocksServer {
get {
@@ -844,7 +844,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [Trojan] Configuration 的本地化字符串。
/// 查找类似 Add [Trojan] 的本地化字符串。
/// </summary>
public static string menuAddTrojanServer {
get {
@@ -853,7 +853,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [TUIC] Configuration 的本地化字符串。
/// 查找类似 Add [TUIC] 的本地化字符串。
/// </summary>
public static string menuAddTuicServer {
get {
@@ -862,7 +862,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [VLESS] Configuration 的本地化字符串。
/// 查找类似 Add [VLESS] 的本地化字符串。
/// </summary>
public static string menuAddVlessServer {
get {
@@ -871,7 +871,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [VMess] Configuration 的本地化字符串。
/// 查找类似 Add [VMess] 的本地化字符串。
/// </summary>
public static string menuAddVmessServer {
get {
@@ -880,7 +880,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [WireGuard] Configuration 的本地化字符串。
/// 查找类似 Add [WireGuard] 的本地化字符串。
/// </summary>
public static string menuAddWireguardServer {
get {
@@ -952,7 +952,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Clone selected Configuration 的本地化字符串。
/// 查找类似 Clone selected 的本地化字符串。
/// </summary>
public static string menuCopyServer {
get {
@@ -970,7 +970,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Edit Configuration 的本地化字符串。
/// 查找类似 Edit 的本地化字符串。
/// </summary>
public static string menuEditServer {
get {
@@ -997,7 +997,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Export selected Configuration for complete configuration 的本地化字符串。
/// 查找类似 Export selected for complete configuration 的本地化字符串。
/// </summary>
public static string menuExport2ClientConfig {
get {
@@ -1006,7 +1006,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Export selected Configuration for complete configuration to clipboard 的本地化字符串。
/// 查找类似 Export selected for complete configuration to clipboard 的本地化字符串。
/// </summary>
public static string menuExport2ClientConfigClipboard {
get {
@@ -1033,7 +1033,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Export Configuration 的本地化字符串。
/// 查找类似 Export 的本地化字符串。
/// </summary>
public static string menuExportConfig {
get {
@@ -1069,7 +1069,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Multi-Configuration Fallback by sing-box 的本地化字符串。
/// 查找类似 Fallback by sing-box 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerSingBoxFallback {
get {
@@ -1078,7 +1078,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
/// 查找类似 LeastPing by sing-box 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerSingBoxLeastPing {
get {
@@ -1087,7 +1087,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Multi-Configuration Fallback by Xray 的本地化字符串。
/// 查找类似 Fallback by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayFallback {
get {
@@ -1096,7 +1096,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
/// 查找类似 LeastLoad by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayLeastLoad {
get {
@@ -1105,7 +1105,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
/// 查找类似 LeastPing by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayLeastPing {
get {
@@ -1114,7 +1114,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
/// 查找类似 Random by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayRandom {
get {
@@ -1123,7 +1123,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
/// 查找类似 RoundRobin by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayRoundRobin {
get {
@@ -1411,7 +1411,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Test Configurations real delay 的本地化字符串。
/// 查找类似 Test real delay 的本地化字符串。
/// </summary>
public static string menuRealPingServer {
get {
@@ -1501,7 +1501,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Remove Child Configuration 的本地化字符串。
/// 查找类似 Remove Child 的本地化字符串。
/// </summary>
public static string menuRemoveChildServer {
get {
@@ -1510,7 +1510,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Remove duplicate Configurations 的本地化字符串。
/// 查找类似 Remove duplicate 的本地化字符串。
/// </summary>
public static string menuRemoveDuplicateServer {
get {
@@ -1528,7 +1528,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Remove selected Configurations 的本地化字符串。
/// 查找类似 Remove selected 的本地化字符串。
/// </summary>
public static string menuRemoveServer {
get {
@@ -1672,7 +1672,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Configurations 的本地化字符串。
/// 查找类似 Configuration 的本地化字符串。
/// </summary>
public static string menuServers {
get {
@@ -1681,7 +1681,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Set as active Configuration 的本地化字符串。
/// 查找类似 Set as active 的本地化字符串。
/// </summary>
public static string menuSetDefaultServer {
get {
@@ -1699,7 +1699,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Share Configuration 的本地化字符串。
/// 查找类似 Share 的本地化字符串。
/// </summary>
public static string menuShareServer {
get {
@@ -1726,7 +1726,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Test Configurations download speed 的本地化字符串。
/// 查找类似 Test download speed 的本地化字符串。
/// </summary>
public static string menuSpeedServer {
get {
@@ -1870,7 +1870,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Test Configurations with tcping 的本地化字符串。
/// 查找类似 Test tcping 的本地化字符串。
/// </summary>
public static string menuTcpingServer {
get {
@@ -1978,7 +1978,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Configuration filter, press Enter to execute 的本地化字符串。
/// 查找类似 Filter, press Enter to execute 的本地化字符串。
/// </summary>
public static string MsgServerTitle {
get {
@@ -2275,7 +2275,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Are you sure you want to remove the Configuration? 的本地化字符串。
/// 查找类似 Are you sure you want to remove? 的本地化字符串。
/// </summary>
public static string RemoveServer {
get {
@@ -4105,7 +4105,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Tray right-click menu Configurations display limit 的本地化字符串。
/// 查找类似 Tray right-click menu display limit 的本地化字符串。
/// </summary>
public static string TbSettingsTrayMenuServersLimit {
get {

View File

@@ -271,7 +271,7 @@
<value>Configurations deduplication completed. Old: {0}, New: {1}.</value>
</data>
<data name="RemoveServer" xml:space="preserve">
<value>Are you sure you want to remove the Configuration?</value>
<value>Are you sure you want to remove?</value>
</data>
<data name="SaveClientConfigurationIn" xml:space="preserve">
<value>The client configuration file is saved at: {0}</value>
@@ -397,7 +397,7 @@
<value>Local</value>
</data>
<data name="MsgServerTitle" xml:space="preserve">
<value>Configuration filter, press Enter to execute</value>
<value>Filter, press Enter to execute</value>
</data>
<data name="menuCheckUpdate" xml:space="preserve">
<value>Check Update</value>
@@ -427,7 +427,7 @@
<value>Routing Setting</value>
</data>
<data name="menuServers" xml:space="preserve">
<value>Configurations</value>
<value>Configuration</value>
</data>
<data name="menuSetting" xml:space="preserve">
<value>Settings</value>
@@ -478,55 +478,55 @@
<value>Scan QR code on the screen</value>
</data>
<data name="menuCopyServer" xml:space="preserve">
<value>Clone selected Configuration</value>
<value>Clone selected</value>
</data>
<data name="menuRemoveDuplicateServer" xml:space="preserve">
<value>Remove duplicate Configurations</value>
<value>Remove duplicate</value>
</data>
<data name="menuRemoveServer" xml:space="preserve">
<value>Remove selected Configurations</value>
<value>Remove selected</value>
</data>
<data name="menuSetDefaultServer" xml:space="preserve">
<value>Set as active Configuration</value>
<value>Set as active</value>
</data>
<data name="menuClearServerStatistics" xml:space="preserve">
<value>Clear all service statistics</value>
</data>
<data name="menuRealPingServer" xml:space="preserve">
<value>Test Configurations real delay</value>
<value>Test real delay</value>
</data>
<data name="menuSortServerResult" xml:space="preserve">
<value>Sort by test result</value>
</data>
<data name="menuSpeedServer" xml:space="preserve">
<value>Test Configurations download speed</value>
<value>Test download speed</value>
</data>
<data name="menuTcpingServer" xml:space="preserve">
<value>Test Configurations with tcping</value>
<value>Test tcping</value>
</data>
<data name="menuExport2ClientConfig" xml:space="preserve">
<value>Export selected Configuration for complete configuration</value>
<value>Export selected for complete configuration</value>
</data>
<data name="menuExport2ShareUrl" xml:space="preserve">
<value>Export Share Link to Clipboard</value>
</data>
<data name="menuAddCustomServer" xml:space="preserve">
<value>Add a custom configuration Configuration</value>
<value>Add a custom configuration</value>
</data>
<data name="menuAddShadowsocksServer" xml:space="preserve">
<value>Add [Shadowsocks] Configuration</value>
<value>Add [Shadowsocks] </value>
</data>
<data name="menuAddSocksServer" xml:space="preserve">
<value>Add [SOCKS] Configuration</value>
<value>Add [SOCKS] </value>
</data>
<data name="menuAddTrojanServer" xml:space="preserve">
<value>Add [Trojan] Configuration</value>
<value>Add [Trojan] </value>
</data>
<data name="menuAddVlessServer" xml:space="preserve">
<value>Add [VLESS] Configuration</value>
<value>Add [VLESS] </value>
</data>
<data name="menuAddVmessServer" xml:space="preserve">
<value>Add [VMess] Configuration</value>
<value>Add [VMess] </value>
</data>
<data name="menuSelectAll" xml:space="preserve">
<value>Select all</value>
@@ -748,7 +748,7 @@
<value>System proxy settings</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Tray right-click menu Configurations display limit</value>
<value>Tray right-click menu display limit</value>
</data>
<data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>Enable UDP</value>
@@ -781,7 +781,7 @@
<value>PAC mode</value>
</data>
<data name="menuShareServer" xml:space="preserve">
<value>Share Configuration</value>
<value>Share</value>
</data>
<data name="menuRouting" xml:space="preserve">
<value>Routing</value>
@@ -922,7 +922,7 @@
<value>Skip test</value>
</data>
<data name="menuEditServer" xml:space="preserve">
<value>Edit Configuration</value>
<value>Edit </value>
</data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Double-clicking Configuration makes it active</value>
@@ -1036,7 +1036,7 @@
<value>Domain</value>
</data>
<data name="menuAddHysteria2Server" xml:space="preserve">
<value>Add [Hysteria2] Configuration</value>
<value>Add [Hysteria2] </value>
</data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria Max bandwidth (Up/Down)</value>
@@ -1045,16 +1045,16 @@
<value>Use System Hosts</value>
</data>
<data name="menuAddTuicServer" xml:space="preserve">
<value>Add [TUIC] Configuration</value>
<value>Add [TUIC] </value>
</data>
<data name="TbHeaderType8" xml:space="preserve">
<value>Congestion control</value>
</data>
<data name="LvPrevProfile" xml:space="preserve">
<value>Previous proxy Configuration remarks</value>
<value>Previous proxy remarks</value>
</data>
<data name="LvNextProfile" xml:space="preserve">
<value>Next proxy Configuration remarks</value>
<value>Next proxy remarks</value>
</data>
<data name="LvPrevProfileTip" xml:space="preserve">
<value>Please make sure the Configuration remarks exist and are unique</value>
@@ -1078,7 +1078,7 @@
<value>Enable IPv6 Address</value>
</data>
<data name="menuAddWireguardServer" xml:space="preserve">
<value>Add [WireGuard] Configuration</value>
<value>Add [WireGuard] </value>
</data>
<data name="TbPrivateKey" xml:space="preserve">
<value>Private Key</value>
@@ -1111,7 +1111,7 @@
<value>*grpc Authority</value>
</data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>Add [HTTP] Configuration</value>
<value>Add [HTTP]</value>
</data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>which conflicts with the group previous proxy</value>
@@ -1225,7 +1225,7 @@
<value>Export Base64-encoded Share Links to Clipboard</value>
</data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>Export selected Configuration for complete configuration to clipboard</value>
<value>Export selected for complete configuration to clipboard</value>
</data>
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Show or hide the main window</value>
@@ -1381,22 +1381,22 @@
<value>Generate Policy Group from Multiple Profiles</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Multi-Configuration Random by Xray</value>
<value>Random by Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Multi-Configuration RoundRobin by Xray</value>
<value>RoundRobin by Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>Multi-Configuration LeastPing by Xray</value>
<value>LeastPing by Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Multi-Configuration LeastLoad by Xray</value>
<value>LeastLoad by Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>Multi-Configuration LeastPing by sing-box</value>
<value>LeastPing by sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve">
<value>Export Configuration</value>
<value>Export</value>
</data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>Current connection info test URL</value>
@@ -1411,7 +1411,7 @@
<value>Mldsa65Verify</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>Add [Anytls] Configuration</value>
<value>Add [Anytls]</value>
</data>
<data name="TbRemoteDNS" xml:space="preserve">
<value>Remote DNS</value>
@@ -1528,16 +1528,16 @@
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Add Policy Group Configuration</value>
<value>Add Policy Group </value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Add Proxy Chain Configuration</value>
<value>Add Proxy Chain</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Add Child Configuration</value>
<value>Add Child </value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Remove Child Configuration</value>
<value>Remove Child </value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>Configuration List</value>
@@ -1546,10 +1546,10 @@
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
<value>Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
<value>Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value>

View File

@@ -26,6 +26,7 @@ public partial class CoreConfigSingboxService
}
await GenOutboundMux(node, outbound);
await GenOutboundTransport(node, outbound);
break;
}
case EConfigType.Shadowsocks:
@@ -33,6 +34,50 @@ public partial class CoreConfigSingboxService
outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None;
outbound.password = node.Id;
if (node.Network == nameof(ETransport.tcp) && node.HeaderType == Global.TcpHeaderHttp)
{
outbound.plugin = "obfs-local";
outbound.plugin_opts = $"obfs=http;obfs-host={node.RequestHost};";
}
else
{
var pluginArgs = string.Empty;
if (node.Network == nameof(ETransport.ws))
{
pluginArgs += "mode=websocket;";
pluginArgs += $"host={node.RequestHost};";
pluginArgs += $"path={node.Path};";
}
else if (node.Network == nameof(ETransport.quic))
{
pluginArgs += "mode=quic;";
}
if (node.StreamSecurity == Global.StreamSecurity)
{
pluginArgs += "tls;";
var certs = CertPemManager.ParsePemChain(node.Cert);
if (certs.Count > 0)
{
var cert = certs.First();
const string beginMarker = "-----BEGIN CERTIFICATE-----\n";
const string endMarker = "\n-----END CERTIFICATE-----";
var base64Content = cert.Replace(beginMarker, "").Replace(endMarker, "").Trim();
// https://github.com/shadowsocks/v2ray-plugin/blob/e9af1cdd2549d528deb20a4ab8d61c5fbe51f306/args.go#L172
// Equal signs and commas [and backslashes] must be escaped with a backslash.
base64Content = base64Content.Replace("=", "\\=");
pluginArgs += $"certRaw={base64Content};";
}
}
if (pluginArgs.Length > 0)
{
outbound.plugin = "v2ray-plugin";
outbound.plugin_opts = pluginArgs;
}
}
await GenOutboundMux(node, outbound);
break;
}
@@ -71,6 +116,8 @@ public partial class CoreConfigSingboxService
{
outbound.flow = node.Flow;
}
await GenOutboundTransport(node, outbound);
break;
}
case EConfigType.Trojan:
@@ -78,6 +125,7 @@ public partial class CoreConfigSingboxService
outbound.password = node.Id;
await GenOutboundMux(node, outbound);
await GenOutboundTransport(node, outbound);
break;
}
case EConfigType.Hysteria2:
@@ -127,8 +175,6 @@ public partial class CoreConfigSingboxService
}
await GenOutboundTls(node, outbound);
await GenOutboundTransport(node, outbound);
}
catch (Exception ex)
{
@@ -232,54 +278,59 @@ public partial class CoreConfigSingboxService
{
try
{
if (node.StreamSecurity is Global.StreamSecurityReality or Global.StreamSecurity)
if (node.StreamSecurity is not (Global.StreamSecurityReality or Global.StreamSecurity))
{
var server_name = string.Empty;
if (node.Sni.IsNotEmpty())
{
server_name = node.Sni;
}
else if (node.RequestHost.IsNotEmpty())
{
server_name = Utils.String2List(node.RequestHost)?.First();
}
var tls = new Tls4Sbox()
return await Task.FromResult(0);
}
if (node.ConfigType is EConfigType.Shadowsocks or EConfigType.SOCKS or EConfigType.WireGuard)
{
return await Task.FromResult(0);
}
var server_name = string.Empty;
if (node.Sni.IsNotEmpty())
{
server_name = node.Sni;
}
else if (node.RequestHost.IsNotEmpty())
{
server_name = Utils.String2List(node.RequestHost)?.First();
}
var tls = new Tls4Sbox()
{
enabled = true,
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
server_name = server_name,
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
alpn = node.GetAlpn(),
};
if (node.Fingerprint.IsNotEmpty())
{
tls.utls = new Utls4Sbox()
{
enabled = true,
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
server_name = server_name,
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
alpn = node.GetAlpn(),
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
};
if (node.Fingerprint.IsNotEmpty())
}
if (node.StreamSecurity == Global.StreamSecurity)
{
var certs = CertPemManager.ParsePemChain(node.Cert);
if (certs.Count > 0)
{
tls.utls = new Utls4Sbox()
{
enabled = true,
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
};
}
if (node.StreamSecurity == Global.StreamSecurity)
{
var certs = CertPemManager.ParsePemChain(node.Cert);
if (certs.Count > 0)
{
tls.certificate = certs;
tls.insecure = false;
}
}
else if (node.StreamSecurity == Global.StreamSecurityReality)
{
tls.reality = new Reality4Sbox()
{
enabled = true,
public_key = node.PublicKey,
short_id = node.ShortId
};
tls.certificate = certs;
tls.insecure = false;
}
outbound.tls = tls;
}
else if (node.StreamSecurity == Global.StreamSecurityReality)
{
tls.reality = new Reality4Sbox()
{
enabled = true,
public_key = node.PublicKey,
short_id = node.ShortId
};
tls.insecure = false;
}
outbound.tls = tls;
}
catch (Exception ex)
{
@@ -305,23 +356,43 @@ public partial class CoreConfigSingboxService
case nameof(ETransport.tcp): //http
if (node.HeaderType == Global.TcpHeaderHttp)
{
if (node.ConfigType == EConfigType.Shadowsocks)
{
outbound.plugin = "obfs-local";
outbound.plugin_opts = $"obfs=http;obfs-host={node.RequestHost};";
}
else
{
transport.type = nameof(ETransport.http);
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
}
transport.type = nameof(ETransport.http);
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
}
break;
case nameof(ETransport.ws):
transport.type = nameof(ETransport.ws);
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
var wsPath = node.Path;
// Parse eh and ed parameters from path using regex
if (!wsPath.IsNullOrEmpty())
{
var edRegex = new Regex(@"[?&]ed=(\d+)");
var edMatch = edRegex.Match(wsPath);
if (edMatch.Success && int.TryParse(edMatch.Groups[1].Value, out var edValue))
{
transport.max_early_data = edValue;
transport.early_data_header_name = "Sec-WebSocket-Protocol";
wsPath = edRegex.Replace(wsPath, "");
wsPath = wsPath.Replace("?&", "?");
if (wsPath.EndsWith('?'))
{
wsPath = wsPath.TrimEnd('?');
}
}
var ehRegex = new Regex(@"[?&]eh=([^&]+)");
var ehMatch = ehRegex.Match(wsPath);
if (ehMatch.Success)
{
transport.early_data_header_name = Uri.UnescapeDataString(ehMatch.Groups[1].Value);
}
}
transport.path = wsPath.IsNullOrEmpty() ? null : wsPath;
if (node.RequestHost.IsNotEmpty())
{
transport.headers = new()

View File

@@ -351,7 +351,6 @@ public partial class CoreConfigV2rayService
if (host.IsNotEmpty())
{
wsSettings.host = host;
wsSettings.headers.Host = host;
}
if (path.IsNotEmpty())
{

View File

@@ -7,7 +7,7 @@ namespace ServiceLib.Services;
/// </summary>
public class DownloadService
{
public event EventHandler<RetResult>? UpdateCompleted;
public event EventHandler<UpdateResult>? UpdateCompleted;
public event ErrorEventHandler? Error;
@@ -40,10 +40,10 @@ public class DownloadService
{
try
{
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
UpdateCompleted?.Invoke(this, new UpdateResult(false, $"{ResUI.Downloading} {url}"));
var progress = new Progress<double>();
progress.ProgressChanged += (sender, value) => UpdateCompleted?.Invoke(this, new RetResult(value > 100, $"...{value}%"));
progress.ProgressChanged += (sender, value) => UpdateCompleted?.Invoke(this, new UpdateResult(value > 100, $"...{value}%"));
var webProxy = await GetWebProxy(blProxy);
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,

View File

@@ -37,7 +37,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN));
await UpdateFunc(false, result.Msg);
url = result.Data?.ToString();
url = result.Url.ToString();
fileName = Utils.GetTempPath(Utils.GetGuid());
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
}
@@ -86,7 +86,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type));
await UpdateFunc(false, result.Msg);
url = result.Data?.ToString();
url = result.Url.ToString();
var ext = url.Contains(".tar.gz") ? ".tar.gz" : Path.GetExtension(url);
fileName = Utils.GetTempPath(Utils.GetGuid() + ext);
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
@@ -110,26 +110,26 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
#region CheckUpdate private
private async Task<RetResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease)
private async Task<UpdateResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease)
{
try
{
var result = await GetRemoteVersion(downloadHandle, type, preRelease);
if (!result.Success || result.Data is null)
if (!result.Success || result.Version is null)
{
return result;
}
return await ParseDownloadUrl(type, (SemanticVersion)result.Data);
return await ParseDownloadUrl(type, result);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
await UpdateFunc(false, ex.Message);
return new RetResult(false, ex.Message);
return new UpdateResult(false, ex.Message);
}
}
private async Task<RetResult> GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease)
private async Task<UpdateResult> GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease)
{
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
var tagName = string.Empty;
@@ -139,7 +139,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
var result = await downloadHandle.TryDownloadString(url, true, Global.AppName);
if (result.IsNullOrEmpty())
{
return new RetResult(false, "");
return new UpdateResult(false, "");
}
var gitHubReleases = JsonUtils.Deserialize<List<GitHubRelease>>(result);
@@ -153,12 +153,12 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
var lastUrl = await downloadHandle.UrlRedirectAsync(url, true);
if (lastUrl == null)
{
return new RetResult(false, "");
return new UpdateResult(false, "");
}
tagName = lastUrl?.Split("/tag/").LastOrDefault();
}
return new RetResult(true, "", new SemanticVersion(tagName));
return new UpdateResult(true, new SemanticVersion(tagName));
}
private async Task<SemanticVersion> GetCoreVersion(ECoreType type)
@@ -213,10 +213,11 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
}
}
private async Task<RetResult> ParseDownloadUrl(ECoreType type, SemanticVersion version)
private async Task<UpdateResult> ParseDownloadUrl(ECoreType type, UpdateResult result)
{
try
{
var version = result.Version ?? new SemanticVersion(0, 0, 0);
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
var coreUrl = await GetUrlFromCore(coreInfo) ?? string.Empty;
SemanticVersion curVersion;
@@ -260,16 +261,17 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
if (curVersion >= version && version != new SemanticVersion(0, 0, 0))
{
return new RetResult(false, message);
return new UpdateResult(false, message);
}
return new RetResult(true, "", url);
result.Url = url;
return result;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
await UpdateFunc(false, ex.Message);
return new RetResult(false, ex.Message);
return new UpdateResult(false, ex.Message);
}
}

View File

@@ -553,6 +553,11 @@ public class ProfilesViewModel : MyReactiveObject
private async Task RemoveDuplicateServer()
{
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
{
return;
}
var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId);
if (tuple.Item1 > 0 || tuple.Item2 > 0)
{

View File

@@ -9,8 +9,8 @@
xmlns:view="using:v2rayN.Desktop.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN"
Width="1000"
Height="600"
Width="1200"
Height="800"
MinWidth="900"
x:DataType="vms:MainWindowViewModel"
Icon="/Assets/NotifyIcon1.ico"
@@ -90,13 +90,13 @@
<MenuItem x:Name="menuOpenTheFileLocation" Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
</MenuItem>
<MenuItem x:Name="menuReload" Header="{x:Static resx:ResUI.menuReload}" />
<MenuItem x:Name="menuHelp" Header="{x:Static resx:ResUI.menuHelp}">
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
<Separator />
</MenuItem>
<MenuItem x:Name="menuReload" Header="{x:Static resx:ResUI.menuReload}" />
<MenuItem x:Name="menuPromotion" Header="{x:Static resx:ResUI.menuPromotion}" />
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuExit}" />

View File

@@ -1,4 +1,4 @@
<reactiveui:ReactiveUserControl
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.BackupAndRestoreView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -23,15 +23,6 @@
</UserControl.Resources>
<DockPanel Margin="{StaticResource Margin8}">
<DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Bottom">
<Button
Width="100"
Margin="{StaticResource Margin8}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="{x:Static resx:ResUI.menuClose}"
DockPanel.Dock="Right"
IsCancel="True"
Style="{StaticResource DefButton}" />
<TextBlock
x:Name="txtMsg"
Margin="{StaticResource Margin8}"
@@ -252,4 +243,4 @@
</materialDesign:Card>
</StackPanel>
</DockPanel>
</reactiveui:ReactiveUserControl>
</reactiveui:ReactiveUserControl>

View File

@@ -1,4 +1,4 @@
<reactiveui:ReactiveUserControl
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.CheckUpdateView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -39,15 +39,6 @@
Content="{x:Static resx:ResUI.menuCheckUpdate}"
IsDefault="True"
Style="{StaticResource DefButton}" />
<Button
Width="100"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Right"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="{x:Static resx:ResUI.menuClose}"
IsCancel="True"
Style="{StaticResource DefButton}" />
</StackPanel>
<StackPanel>
@@ -99,4 +90,4 @@
</ListView>
</StackPanel>
</DockPanel>
</reactiveui:ReactiveUserControl>
</reactiveui:ReactiveUserControl>

View File

@@ -11,8 +11,8 @@
xmlns:view="clr-namespace:v2rayN.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN"
Width="900"
Height="700"
Width="1200"
Height="800"
MinWidth="900"
x:TypeArguments="vms:MainWindowViewModel"
Icon="/Resources/v2rayN.ico"
@@ -32,6 +32,7 @@
<materialDesign:DialogHost
materialDesign:TransitionAssist.DisableTransitions="True"
CloseOnClickAway="True"
Identifier="RootDialog"
SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}"
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
@@ -231,23 +232,6 @@
</MenuItem>
</Menu>
<Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem
x:Name="menuReload"
Padding="{StaticResource MarginLeftRight8}"
AutomationProperties.Name="{x:Static resx:ResUI.menuReload}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
Margin="{StaticResource MarginRight8}"
VerticalAlignment="Center"
Kind="Reload" />
<TextBlock Text="{x:Static resx:ResUI.menuReload}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
</Menu>
<Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem
x:Name="menuHelp"
@@ -267,6 +251,23 @@
</MenuItem>
</Menu>
<Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem
x:Name="menuReload"
Padding="{StaticResource MarginLeftRight8}"
AutomationProperties.Name="{x:Static resx:ResUI.menuReload}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
Margin="{StaticResource MarginRight8}"
VerticalAlignment="Center"
Kind="Reload" />
<TextBlock Text="{x:Static resx:ResUI.menuReload}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
</Menu>
<Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem
x:Name="menuPromotion"

View File

@@ -15,18 +15,7 @@
<sys:Double x:Key="QrcodeWidth">400</sys:Double>
</UserControl.Resources>
<DockPanel Margin="{StaticResource Margin8}">
<StackPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Bottom">
<Button
Width="100"
HorizontalAlignment="Right"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="{x:Static resx:ResUI.menuClose}"
IsCancel="True"
IsDefault="True"
Style="{StaticResource DefButton}" />
</StackPanel>
<StackPanel Margin="{StaticResource Margin8}">
<Grid Margin="{StaticResource Margin8}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -51,5 +40,5 @@
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</Grid>
</DockPanel>
</StackPanel>
</UserControl>

View File

@@ -20,6 +20,7 @@
mc:Ignorable="d">
<materialDesign:DialogHost
materialDesign:TransitionAssist.DisableTransitions="True"
CloseOnClickAway="True"
Identifier="SubDialog"
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
<DockPanel>