Compare commits

..

2 commits

Author SHA1 Message Date
renwofei423
ad9e9ef351
Merge 3743aa29cb into a2929c6086 2026-03-20 14:35:40 +08:00
wuzhou
3743aa29cb feat(send-through): 支持配置本地出站地址
为 Xray 增加 SendThrough 配置项,允许指定本机 IPv4 作为出站源地址。

- 在核心设置页新增 SendThrough 输入框及中英文提示文案
- 保存配置时校验并持久化本机 IPv4 地址
- 生成 Xray 配置时为所有 outbound 写入 sendThrough 字段

影响说明:
- 仅对 Xray 生效,留空时不设置该字段
2026-03-16 15:07:56 +08:00
65 changed files with 442 additions and 2042 deletions

View file

@ -253,14 +253,11 @@ download_xray() {
}
download_singbox() {
# Download sing-box
local outdir="$1" rid="$2" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin cronet
local outdir="$1" rid="$2" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
mkdir -p "$outdir"
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' \
| sed -E 's/.*"v([^"]+)".*/\1/' \
| head -n1)" || true
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
[[ -n "$ver" ]] || { echo "[sing-box] Failed to get version"; return 1; }
if [[ "$rid" == "linux-arm64" ]]; then
@ -275,8 +272,6 @@ download_singbox() {
bin="$(find "$tmp" -type f -name 'sing-box' | head -n1 || true)"
[[ -n "$bin" ]] || { echo "[!] sing-box unpack failed"; rm -rf "$tmp"; return 1; }
install -m 755 "$bin" "$outdir/sing-box"
cronet="$(find "$tmp" -type f -name 'libcronet*.so*' | head -n1 || true)"
[[ -n "$cronet" ]] && install -m 644 "$cronet" "$outdir/libcronet.so"
rm -rf "$tmp"
}

View file

@ -232,13 +232,11 @@ download_xray() {
download_singbox() {
# Download sing-box
local outdir="$1" rid="$2" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin cronet
local outdir="$1" rid="$2" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
mkdir -p "$outdir"
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' \
| sed -E 's/.*"v([^"]+)".*/\1/' \
| head -n1)" || true
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
[[ -n "$ver" ]] || { echo "[sing-box] Failed to get version"; return 1; }
if [[ "$rid" == "linux-arm64" ]]; then
@ -253,8 +251,6 @@ download_singbox() {
bin="$(find "$tmp" -type f -name 'sing-box' | head -n1 || true)"
[[ -n "$bin" ]] || { echo "[!] sing-box unpack failed"; rm -rf "$tmp"; return 1; }
install -m 755 "$bin" "$outdir/sing-box"
cronet="$(find "$tmp" -type f -name 'libcronet*.so*' | head -n1 || true)"
[[ -n "$cronet" ]] && install -m 644 "$cronet" "$outdir/libcronet.so"
rm -rf "$tmp"
}

View file

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.20.2</Version>
<Version>7.19.4</Version>
</PropertyGroup>
<PropertyGroup>

View file

@ -6,29 +6,26 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.4.1" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.13" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.13" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.12" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.12" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.4.12" />
<PackageVersion Include="CliWrap" Version="3.10.1" />
<PackageVersion Include="CliWrap" Version="3.10.0" />
<PackageVersion Include="Downloader" Version="5.1.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.1" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" />
<PackageVersion Include="QRCoder" Version="1.8.0" />
<PackageVersion Include="ReactiveUI" Version="23.2.1" />
<PackageVersion Include="QRCoder" Version="1.7.0" />
<PackageVersion Include="ReactiveUI" Version="23.1.8" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="23.2.1" />
<PackageVersion Include="ReactiveUI.WPF" Version="23.1.8" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.3" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.2" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.3" />
<PackageVersion Include="NLog" Version="6.1.2" />
<PackageVersion Include="NLog" Version="6.1.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.4" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup>

@ -1 +1 @@
Subproject commit 50f615b671ff8d4a6a850aed19da5f94f58b5d96
Subproject commit ffb2850df0991495d0918e13cc5701737f26175a

View file

@ -1,228 +0,0 @@
using System.Text.Json.Nodes;
using ServiceLib;
using ServiceLib.Enums;
using ServiceLib.Models;
using ServiceLib.Services.CoreConfig;
using Xunit;
namespace ServiceLib.Tests;
public class CoreConfigV2rayServiceTests
{
private const string SendThrough = "198.51.100.10";
[Fact]
public void GenerateClientConfigContent_OnlyAppliesSendThroughToRemoteProxyOutbounds()
{
var node = CreateProxyNode("proxy-1", "198.51.100.1", 443);
var service = new CoreConfigV2rayService(CreateContext(node));
var result = service.GenerateClientConfigContent();
Assert.True(result.Success);
var outbounds = GetOutbounds(result.Data?.ToString());
var proxyOutbound = outbounds.Single(outbound => outbound["tag"]!.GetValue<string>() == Global.ProxyTag);
var directOutbound = outbounds.Single(outbound => outbound["tag"]!.GetValue<string>() == Global.DirectTag);
var blockOutbound = outbounds.Single(outbound => outbound["tag"]!.GetValue<string>() == Global.BlockTag);
Assert.Equal(SendThrough, proxyOutbound["sendThrough"]?.GetValue<string>());
Assert.Null(directOutbound["sendThrough"]);
Assert.Null(blockOutbound["sendThrough"]);
}
[Fact]
public void GenerateClientConfigContent_OnlyAppliesSendThroughToChainExitOutbounds()
{
var exitNode = CreateProxyNode("exit", "198.51.100.2", 443);
var entryNode = CreateProxyNode("entry", "198.51.100.3", 443);
var chainNode = CreateChainNode("chain", exitNode, entryNode);
var service = new CoreConfigV2rayService(CreateContext(
chainNode,
allProxiesMap: new Dictionary<string, ProfileItem>
{
[exitNode.IndexId] = exitNode,
[entryNode.IndexId] = entryNode,
}));
var result = service.GenerateClientConfigContent();
Assert.True(result.Success);
var outbounds = GetOutbounds(result.Data?.ToString())
.Where(outbound => outbound["protocol"]?.GetValue<string>() is not ("freedom" or "blackhole" or "dns"))
.ToList();
var sendThroughOutbounds = outbounds
.Where(outbound => outbound["sendThrough"]?.GetValue<string>() == SendThrough)
.ToList();
var chainedOutbounds = outbounds
.Where(outbound => outbound["streamSettings"]?["sockopt"]?["dialerProxy"] is not null)
.ToList();
Assert.Single(sendThroughOutbounds);
Assert.All(chainedOutbounds, outbound => Assert.Null(outbound["sendThrough"]));
}
[Fact]
public void GenerateClientConfigContent_DoesNotApplySendThroughToTunRelayLoopbackOutbound()
{
var node = CreateProxyNode("proxy-1", "198.51.100.4", 443);
var config = CreateConfig();
config.TunModeItem.EnableLegacyProtect = false;
var service = new CoreConfigV2rayService(CreateContext(
node,
config,
isTunEnabled: true,
tunProtectSsPort: 10811,
proxyRelaySsPort: 10812));
var result = service.GenerateClientConfigContent();
Assert.True(result.Success);
var outbounds = GetOutbounds(result.Data?.ToString());
Assert.DoesNotContain(outbounds, outbound => outbound["sendThrough"]?.GetValue<string>() == SendThrough);
}
private static CoreConfigContext CreateContext(
ProfileItem node,
Config? config = null,
Dictionary<string, ProfileItem>? allProxiesMap = null,
bool isTunEnabled = false,
int tunProtectSsPort = 0,
int proxyRelaySsPort = 0)
{
return new CoreConfigContext
{
Node = node,
RunCoreType = ECoreType.Xray,
AppConfig = config ?? CreateConfig(),
AllProxiesMap = allProxiesMap ?? new(),
SimpleDnsItem = new SimpleDNSItem(),
IsTunEnabled = isTunEnabled,
TunProtectSsPort = tunProtectSsPort,
ProxyRelaySsPort = proxyRelaySsPort,
};
}
private static Config CreateConfig()
{
return new Config
{
IndexId = string.Empty,
SubIndexId = string.Empty,
CoreBasicItem = new()
{
LogEnabled = false,
Loglevel = "warning",
MuxEnabled = false,
DefAllowInsecure = false,
DefFingerprint = Global.Fingerprints.First(),
DefUserAgent = string.Empty,
SendThrough = SendThrough,
EnableFragment = false,
EnableCacheFile4Sbox = true,
},
TunModeItem = new()
{
EnableTun = false,
AutoRoute = true,
StrictRoute = true,
Stack = string.Empty,
Mtu = 9000,
EnableIPv6Address = false,
IcmpRouting = Global.TunIcmpRoutingPolicies.First(),
EnableLegacyProtect = false,
},
KcpItem = new(),
GrpcItem = new(),
RoutingBasicItem = new()
{
DomainStrategy = Global.DomainStrategies.First(),
DomainStrategy4Singbox = Global.DomainStrategies4Sbox.First(),
RoutingIndexId = string.Empty,
},
GuiItem = new(),
MsgUIItem = new(),
UiItem = new()
{
CurrentLanguage = "en",
CurrentFontFamily = string.Empty,
MainColumnItem = [],
WindowSizeItem = [],
},
ConstItem = new(),
SpeedTestItem = new(),
Mux4RayItem = new()
{
Concurrency = 8,
XudpConcurrency = 8,
XudpProxyUDP443 = "reject",
},
Mux4SboxItem = new()
{
Protocol = string.Empty,
},
HysteriaItem = new(),
ClashUIItem = new()
{
ConnectionsColumnItem = [],
},
SystemProxyItem = new(),
WebDavItem = new(),
CheckUpdateItem = new(),
Fragment4RayItem = null,
Inbound = [new InItem
{
Protocol = EInboundProtocol.socks.ToString(),
LocalPort = 10808,
UdpEnabled = true,
SniffingEnabled = true,
RouteOnly = false,
}],
GlobalHotkeys = [],
CoreTypeItem = [],
SimpleDNSItem = new(),
};
}
private static ProfileItem CreateProxyNode(string indexId, string address, int port)
{
return new ProfileItem
{
IndexId = indexId,
Remarks = indexId,
ConfigType = EConfigType.SOCKS,
CoreType = ECoreType.Xray,
Address = address,
Port = port,
};
}
private static ProfileItem CreateChainNode(string indexId, params ProfileItem[] nodes)
{
var chainNode = new ProfileItem
{
IndexId = indexId,
Remarks = indexId,
ConfigType = EConfigType.ProxyChain,
CoreType = ECoreType.Xray,
};
chainNode.SetProtocolExtra(new ProtocolExtraItem
{
ChildItems = string.Join(',', nodes.Select(node => node.IndexId)),
});
return chainNode;
}
private static List<JsonObject> GetOutbounds(string? json)
{
var root = JsonNode.Parse(json ?? throw new InvalidOperationException("Config JSON is missing"))?.AsObject()
?? throw new InvalidOperationException("Failed to parse config JSON");
return root["outbounds"]?.AsArray().Select(node => node!.AsObject()).ToList()
?? throw new InvalidOperationException("Config JSON does not contain outbounds");
}
}

View file

@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ServiceLib\ServiceLib.csproj" />
</ItemGroup>
</Project>

View file

@ -46,7 +46,7 @@ public static class ProcUtils
return null;
}
public static bool RebootAsAdmin(bool blAdmin = true)
public static void RebootAsAdmin(bool blAdmin = true)
{
try
{
@ -58,12 +58,11 @@ public static class ProcUtils
FileName = Utils.GetExePath().AppendQuotes(),
Verb = blAdmin ? "runas" : null,
};
return Process.Start(startInfo) != null;
_ = Process.Start(startInfo);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return false;
}
}
}

View file

@ -13,7 +13,6 @@ public enum EConfigType
WireGuard = 9,
HTTP = 10,
Anytls = 11,
Naive = 12,
PolicyGroup = 101,
ProxyChain = 102,
}

View file

@ -182,22 +182,17 @@ public class Global
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/"
];
public static readonly Dictionary<string, string> TcpHttpUserAgentTexts = new()
public static readonly Dictionary<string, string> UserAgentTexts = new()
{
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
{"none",""},
{"golang",""}
{"none",""}
};
public const string Hysteria2ProtocolShare = "hy2://";
public const string NaiveHttpsProtocolShare = "naive+https://";
public const string NaiveQuicProtocolShare = "naive+quic://";
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
{
{ EConfigType.VMess, "vmess://" },
@ -208,8 +203,7 @@ public class Global
{ EConfigType.Hysteria2, "hysteria2://" },
{ EConfigType.TUIC, "tuic://" },
{ EConfigType.WireGuard, "wireguard://" },
{ EConfigType.Anytls, "anytls://" },
{ EConfigType.Naive, "naive://" }
{ EConfigType.Anytls, "anytls://" }
};
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
@ -223,8 +217,7 @@ public class Global
{ EConfigType.Hysteria2, "hysteria2" },
{ EConfigType.TUIC, "tuic" },
{ EConfigType.WireGuard, "wireguard" },
{ EConfigType.Anytls, "anytls" },
{ EConfigType.Naive, "naive" }
{ EConfigType.Anytls, "anytls" }
};
public static readonly List<string> VmessSecurities =
@ -349,7 +342,6 @@ public class Global
EConfigType.Hysteria2,
EConfigType.TUIC,
EConfigType.Anytls,
EConfigType.Naive,
EConfigType.WireGuard,
EConfigType.SOCKS,
EConfigType.HTTP,
@ -392,8 +384,9 @@ public class Global
[
"chrome",
"firefox",
"safari",
"edge",
"golang"
"none"
];
public static readonly List<string> XhttpMode =
@ -565,14 +558,6 @@ public class Global
"bbr"
];
public static readonly List<string> NaiveCongestionControls =
[
"bbr",
"bbr2",
"cubic",
"reno"
];
public static readonly List<string> allowSelectType =
[
"selector",
@ -675,14 +660,5 @@ public class Global
""
];
public static readonly List<string> TunIcmpRoutingPolicies =
[
"rule",
"direct",
"unreachable",
"drop",
"reply",
];
#endregion const
}

View file

@ -309,11 +309,6 @@ public class CoreConfigContextBuilder
}
var nodeValidatorResult = NodeValidator.Validate(node, context.RunCoreType);
var msgs = new List<string>([.. nodeValidatorResult.Errors, .. nodeValidatorResult.Warnings]);
if (msgs.Count > 0)
{
Logging.SaveLog($"{node.Remarks}: {string.Join("; ", msgs)}");
}
if (!nodeValidatorResult.Success)
{
return nodeValidatorResult;
@ -327,7 +322,6 @@ public class CoreConfigContextBuilder
context.ProtectDomainList.Add(address);
}
// ech query server name protect
if (!node.EchConfigList.IsNullOrEmpty())
{
var echQuerySni = node.Sni;
@ -344,20 +338,6 @@ public class CoreConfigContextBuilder
}
}
// xhttp downloadSettings address protect
if (!string.IsNullOrEmpty(node.Extra)
&& JsonUtils.ParseJson(node.Extra) is JsonObject extra
&& extra.TryGetPropertyValue("downloadSettings", out var dsNode)
&& dsNode is JsonObject downloadSettings
&& downloadSettings.TryGetPropertyValue("address", out var dAddrNode)
&& dAddrNode is JsonValue dAddrValue
&& dAddrValue.TryGetValue(out string? dAddr)
&& !string.IsNullOrEmpty(dAddr)
&& Utils.IsDomain(dAddr))
{
context.ProtectDomainList.Add(dAddr);
}
return nodeValidatorResult;
}

View file

@ -143,19 +143,11 @@ public class NodeValidator
if (item.Network == nameof(ETransport.xhttp) && !item.Extra.IsNullOrEmpty())
{
if (JsonUtils.ParseJson(item.Extra) is not JsonObject)
if (JsonUtils.ParseJson(item.Extra) is null)
{
v.Error(string.Format(ResUI.MsgInvalidProperty, "XHTTP Extra"));
}
}
if (!item.Finalmask.IsNullOrEmpty())
{
if (JsonUtils.ParseJson(item.Finalmask) is not JsonObject)
{
v.Error(string.Format(ResUI.MsgInvalidProperty, "Finalmask"));
}
}
}
private static string? ValidateSingboxTransport(EConfigType configType, string net)

View file

@ -92,8 +92,6 @@ public static class ConfigHandler
{
EnableTun = false,
Mtu = 9000,
IcmpRouting = Global.TunIcmpRoutingPolicies.First(),
EnableLegacyProtect = false,
};
config.GuiItem ??= new();
config.MsgUIItem ??= new();
@ -272,7 +270,6 @@ public static class ConfigHandler
EConfigType.TUIC => await AddTuicServer(config, item),
EConfigType.WireGuard => await AddWireguardServer(config, item),
EConfigType.Anytls => await AddAnytlsServer(config, item),
EConfigType.Naive => await AddNaiveServer(config, item),
_ => -1,
};
return ret;
@ -808,7 +805,7 @@ public static class ConfigHandler
}
/// <summary>
/// Add or edit an Anytls server
/// Add or edit a Anytls server
/// Validates and processes Anytls-specific settings
/// </summary>
/// <param name="config">Current configuration</param>
@ -835,36 +832,6 @@ public static class ConfigHandler
return 0;
}
/// <summary>
/// Add or edit a Naive server
/// Validates and processes Naive-specific settings
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="profileItem">Naive 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> AddNaiveServer(Config config, ProfileItem profileItem, bool toFile = true)
{
profileItem.ConfigType = EConfigType.Naive;
profileItem.CoreType = ECoreType.sing_box;
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Username = profileItem.Username.TrimEx();
profileItem.Password = profileItem.Password.TrimEx();
profileItem.Alpn = string.Empty;
profileItem.Network = string.Empty;
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
profileItem.StreamSecurity = Global.StreamSecurity;
}
if (profileItem.Password.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
@ -1111,8 +1078,7 @@ public static class ConfigHandler
if (toFile)
{
//profileItem.SetProtocolExtra();
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra());
profileItem.SetProtocolExtra();
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
}
return 0;
@ -1140,7 +1106,6 @@ public static class ConfigHandler
&& AreEqual(o.Address, n.Address)
&& o.Port == n.Port
&& AreEqual(o.Password, n.Password)
&& AreEqual(o.Username, n.Username)
&& AreEqual(oProtocolExtra.VlessEncryption, nProtocolExtra.VlessEncryption)
&& AreEqual(oProtocolExtra.SsMethod, nProtocolExtra.SsMethod)
&& AreEqual(oProtocolExtra.VmessSecurity, nProtocolExtra.VmessSecurity)
@ -1416,24 +1381,12 @@ public static class ConfigHandler
/// <returns>A SOCKS profile item or null if not needed</returns>
public static ProfileItem? GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
{
ProfileItem? itemSocks = null;
if (node.ConfigType != EConfigType.Custom
&& coreType != ECoreType.sing_box
&& config.TunModeItem.EnableTun
&& config.TunModeItem.EnableLegacyProtect)
if (node.ConfigType != EConfigType.Custom || !(node.PreSocksPort > 0))
{
itemSocks = new ProfileItem()
{
CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
};
return null;
}
else if (node.ConfigType == EConfigType.Custom
&& node.PreSocksPort is > 0 and <= 65535)
{
var preCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
ProfileItem? itemSocks = null;
var preCoreType = AppManager.Instance.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
{
CoreType = preCoreType,
@ -1441,7 +1394,6 @@ public static class ConfigHandler
Address = Global.Loopback,
Port = node.PreSocksPort.Value,
};
}
return itemSocks;
}
@ -1545,7 +1497,6 @@ public static class ConfigHandler
EConfigType.TUIC => await AddTuicServer(config, profileItem, false),
EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false),
EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false),
EConfigType.Naive => await AddNaiveServer(config, profileItem, false),
_ => -1,
};

View file

@ -19,7 +19,6 @@ public class FmtHandler
EConfigType.TUIC => TuicFmt.ToUri(item),
EConfigType.WireGuard => WireguardFmt.ToUri(item),
EConfigType.Anytls => AnytlsFmt.ToUri(item),
EConfigType.Naive => NaiveFmt.ToUri(item),
_ => null,
};
@ -81,12 +80,6 @@ public class FmtHandler
{
return AnytlsFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Naive])
|| str.StartsWith(Global.NaiveHttpsProtocolShare)
|| str.StartsWith(Global.NaiveQuicProtocolShare))
{
return NaiveFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;

View file

@ -1,91 +0,0 @@
namespace ServiceLib.Handler.Fmt;
public class NaiveFmt : 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.Naive,
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
var protocolExtra = item.GetProtocolExtra();
if (parsedUrl.Scheme.Contains("quic"))
{
protocolExtra = protocolExtra with
{
NaiveQuic = true,
};
}
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
if (rawUserInfo.Contains(':'))
{
var split = rawUserInfo.Split(':', 2);
item.Username = split[0];
item.Password = split[1];
}
else
{
item.Password = rawUserInfo;
}
var query = Utils.ParseQueryString(parsedUrl.Query);
ResolveUriQuery(query, ref item);
var insecureConcurrency = int.TryParse(GetQueryValue(query, "insecure-concurrency"), out var ic) ? ic : 0;
if (insecureConcurrency > 0)
{
protocolExtra = protocolExtra with
{
InsecureConcurrency = insecureConcurrency,
};
}
item.SetProtocolExtra(protocolExtra);
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 userInfo = item.Username.IsNotEmpty() ? $"{Utils.UrlEncode(item.Username)}:{Utils.UrlEncode(item.Password)}" : Utils.UrlEncode(item.Password);
var dicQuery = new Dictionary<string, string>();
ToUriQuery(item, Global.None, ref dicQuery);
var protocolExtra = item.GetProtocolExtra();
if (protocolExtra.InsecureConcurrency > 0)
{
dicQuery.Add("insecure-concurrency", protocolExtra?.InsecureConcurrency.ToString());
}
var query = dicQuery.Count > 0
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()))
: string.Empty;
var url = $"{userInfo}@{GetIpv6(item.Address)}:{item.Port}";
if (protocolExtra.NaiveQuic == true)
{
return $"{Global.NaiveQuicProtocolShare}{url}{query}{remark}";
}
else
{
return $"{Global.NaiveHttpsProtocolShare}{url}{query}{remark}";
}
}
}

View file

@ -30,10 +30,7 @@ public class TuicFmt : BaseFmt
var query = Utils.ParseQueryString(url.Query);
ResolveUriQuery(query, ref item);
item.SetProtocolExtra(item.GetProtocolExtra() with
{
CongestionControl = GetQueryValue(query, "congestion_control")
});
item.HeaderType = GetQueryValue(query, "congestion_control");
return item;
}
@ -54,10 +51,7 @@ public class TuicFmt : BaseFmt
var dicQuery = new Dictionary<string, string>();
ToUriQueryLite(item, ref dicQuery);
if (!item.GetProtocolExtra().CongestionControl.IsNullOrEmpty())
{
dicQuery.Add("congestion_control", item.GetProtocolExtra().CongestionControl);
}
dicQuery.Add("congestion_control", item.HeaderType);
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Username ?? ""}:{item.Password}", dicQuery, remark);
}

View file

@ -305,12 +305,12 @@ public sealed class AppManager
return await SQLiteHelper.Instance.TableAsync<FullConfigTemplateItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
}
#pragma warning disable CS0618
public async Task MigrateProfileExtra()
{
await MigrateProfileExtraGroup();
#pragma warning disable CS0618
const int pageSize = 100;
var offset = 0;
@ -334,6 +334,7 @@ public sealed class AppManager
}
//await ProfileGroupItemManager.Instance.ClearAll();
#pragma warning restore CS0618
}
private async Task<int> MigrateProfileExtraSub(List<ProfileItem> batch)
@ -379,7 +380,6 @@ public sealed class AppManager
break;
case EConfigType.TUIC:
extra = extra with { CongestionControl = item.HeaderType.NullIfEmpty(), };
item.Username = item.Id;
item.Id = item.Security;
item.Password = item.Security;
@ -436,6 +436,7 @@ public sealed class AppManager
private async Task<bool> MigrateProfileExtraGroup()
{
#pragma warning disable CS0618
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
@ -500,9 +501,8 @@ public sealed class AppManager
return true;
//await ProfileGroupItemManager.Instance.ClearAll();
}
#pragma warning restore CS0618
}
#endregion SqliteHelper

View file

@ -90,9 +90,6 @@ public class CoreManager
await CoreStart(mainContext);
await CoreStartPreService(preContext);
AppManager.Instance.RunningCoreType = preContext?.RunCoreType ?? mainContext.RunCoreType;
if (_processService != null)
{
await UpdateFunc(true, $"{node.GetSummary()}");
@ -175,7 +172,7 @@ public class CoreManager
private async Task CoreStart(CoreConfigContext context)
{
var node = context.Node;
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
var coreType = AppManager.Instance.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;

View file

@ -146,8 +146,6 @@ public class TunModeItem
public string Stack { get; set; }
public int Mtu { get; set; }
public bool EnableIPv6Address { get; set; }
public string IcmpRouting { get; set; }
public bool EnableLegacyProtect { get; set; }
}
[Serializable]

View file

@ -2,9 +2,6 @@ namespace ServiceLib.Models;
public record ProtocolExtraItem
{
public bool? Uot { get; init; }
public string? CongestionControl { get; init; }
// vmess
public string? AlterId { get; init; }
public string? VmessSecurity { get; init; }
@ -32,10 +29,6 @@ public record ProtocolExtraItem
public string? Ports { get; init; }
public string? HopInterval { get; init; }
// naiveproxy
public int? InsecureConcurrency { get; init; }
public bool? NaiveQuic { get; init; }
// group profile
public string? GroupType { get; init; }
public string? ChildItems { get; init; }

View file

@ -134,14 +134,10 @@ public class Outbound4Sbox : BaseServer4Sbox
public int? recv_window_conn { get; set; }
public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; }
public int? insecure_concurrency { get; set; }
public bool? udp_over_tcp { get; set; }
public string? method { get; set; }
public string? username { get; set; }
public string? password { get; set; }
public string? congestion_control { get; set; }
public bool? quic { get; set; }
public string? quic_congestion_control { get; set; }
public string? version { get; set; }
public string? network { get; set; }
public string? packet_encoding { get; set; }

View file

@ -181,8 +181,6 @@ public class ServersItem4Ray
public string flow { get; set; }
public bool? uot { get; set; }
public List<SocksUsersItem4Ray> users { get; set; }
}
@ -339,7 +337,7 @@ public class StreamSettings4Ray
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
public object? finalmask { get; set; }
public Finalmask4Ray? finalmask { get; set; }
public Sockopt4Ray? sockopt { get; set; }
}
@ -423,8 +421,6 @@ public class HttpupgradeSettings4Ray
public string? path { get; set; }
public string? host { get; set; }
public Headers4Ray headers { get; set; }
}
public class XhttpSettings4Ray
@ -460,31 +456,33 @@ public class GrpcSettings4Ray
public int? health_check_timeout { get; set; }
public bool? permit_without_stream { get; set; }
public int? initial_windows_size { get; set; }
public string? user_agent { get; set; }
}
public class HysteriaSettings4Ray
{
public int version { get; set; }
public string? auth { get; set; }
public string? up { get; set; }
public string? down { get; set; }
public HysteriaUdpHop4Ray? udphop { get; set; }
}
public class UdpHop4Ray
public class HysteriaUdpHop4Ray
{
public string? ports { get; set; }
public string? port { get; set; }
public string? interval { get; set; }
}
public class Finalmask4Ray
{
public List<Mask4Ray>? tcp { get; set; }
public List<Mask4Ray>? udp { get; set; }
public QuicParams4Ray? quicParams { get; set; }
}
public class Mask4Ray
{
public string type { get; set; }
public MaskSettings4Ray? settings { get; set; }
public object? settings { get; set; }
}
public class MaskSettings4Ray
@ -493,14 +491,6 @@ public class MaskSettings4Ray
public string? domain { get; set; }
}
public class QuicParams4Ray
{
public string? congestion { get; set; }
public string? brutalUp { get; set; }
public string? brutalDown { get; set; }
public UdpHop4Ray? udpHop { get; set; }
}
public class AccountsItem4Ray
{
public string user { get; set; }

View file

@ -735,15 +735,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Add [NaïveProxy] 的本地化字符串。
/// </summary>
public static string menuAddNaiveServer {
get {
return ResourceManager.GetString("menuAddNaiveServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Add Policy Group 的本地化字符串。
/// </summary>
@ -942,42 +933,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Copy 的本地化字符串。
/// </summary>
public static string menuEditCopy {
get {
return ResourceManager.GetString("menuEditCopy", resourceCulture);
}
}
/// <summary>
/// 查找类似 Format 的本地化字符串。
/// </summary>
public static string menuEditFormat {
get {
return ResourceManager.GetString("menuEditFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 Paste 的本地化字符串。
/// </summary>
public static string menuEditPaste {
get {
return ResourceManager.GetString("menuEditPaste", resourceCulture);
}
}
/// <summary>
/// 查找类似 Select all 的本地化字符串。
/// </summary>
public static string menuEditSelectAll {
get {
return ResourceManager.GetString("menuEditSelectAll", resourceCulture);
}
}
/// <summary>
/// 查找类似 Edit 的本地化字符串。
/// </summary>
@ -3087,15 +3042,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 ICMP routing policy 的本地化字符串。
/// </summary>
public static string TbIcmpRoutingPolicy {
get {
return ResourceManager.GetString("TbIcmpRoutingPolicy", resourceCulture);
}
}
/// <summary>
/// 查找类似 UUID(id) 的本地化字符串。
/// </summary>
@ -3132,15 +3078,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Insecure Concurrency 的本地化字符串。
/// </summary>
public static string TbInsecureConcurrency {
get {
return ResourceManager.GetString("TbInsecureConcurrency", resourceCulture);
}
}
/// <summary>
/// 查找类似 Most Stable 的本地化字符串。
/// </summary>
@ -3159,15 +3096,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Legacy TUN Protect 的本地化字符串。
/// </summary>
public static string TbLegacyProtect {
get {
return ResourceManager.GetString("TbLegacyProtect", resourceCulture);
}
}
/// <summary>
/// 查找类似 Address (IPv4, IPv6) 的本地化字符串。
/// </summary>
@ -4014,6 +3942,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 e.g. 192.168.1.10 的本地化字符串。
/// </summary>
public static string TbSettingsSendThroughHint {
get {
return ResourceManager.GetString("TbSettingsSendThroughHint", resourceCulture);
}
}
/// <summary>
/// 查找类似 Local outbound address (SendThrough) 的本地化字符串。
/// </summary>
@ -4554,24 +4491,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 UDP over TCP 的本地化字符串。
/// </summary>
public static string TbUot {
get {
return ResourceManager.GetString("TbUot", resourceCulture);
}
}
/// <summary>
/// 查找类似 Username 的本地化字符串。
/// </summary>
public static string TbUsername {
get {
return ResourceManager.GetString("TbUsername", resourceCulture);
}
}
/// <summary>
/// 查找类似 Validate Regional Domain IPs 的本地化字符串。
/// </summary>

View file

@ -1668,34 +1668,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
<data name="menuEditCopy" xml:space="preserve">
<value>کپی</value>
</data>
<data name="menuEditSelectAll" xml:space="preserve">
<value>انتخاب همه</value>
</data>
<data name="menuEditPaste" xml:space="preserve">
<value>Paste</value>
</data>
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Add NaïveProxy</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
</root>

View file

@ -1665,34 +1665,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
<data name="menuEditCopy" xml:space="preserve">
<value>Copier</value>
</data>
<data name="menuEditSelectAll" xml:space="preserve">
<value>Tout sélect</value>
</data>
<data name="menuEditPaste" xml:space="preserve">
<value>Paste</value>
</data>
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Ajouter [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
</root>

View file

@ -1668,34 +1668,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
<data name="menuEditCopy" xml:space="preserve">
<value>Másolás</value>
</data>
<data name="menuEditSelectAll" xml:space="preserve">
<value>Összes kijelölése</value>
</data>
<data name="menuEditPaste" xml:space="preserve">
<value>Paste</value>
</data>
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>[NaïveProxy] konfiguráció hozzáadása</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
</root>

View file

@ -1323,6 +1323,9 @@
<data name="TbSettingsSendThrough" xml:space="preserve">
<value>Local outbound address (SendThrough)</value>
</data>
<data name="TbSettingsSendThroughHint" xml:space="preserve">
<value>e.g. 192.168.1.10</value>
</data>
<data name="TbSettingsSendThroughTip" xml:space="preserve">
<value>Only applies to Xray. Fill in a local IPv4 address; leave empty to disable.</value>
</data>
@ -1677,34 +1680,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
<data name="menuEditCopy" xml:space="preserve">
<value>Copy</value>
</data>
<data name="menuEditSelectAll" xml:space="preserve">
<value>Select all</value>
</data>
<data name="menuEditPaste" xml:space="preserve">
<value>Paste</value>
</data>
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Add [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
</root>

View file

@ -142,7 +142,7 @@
<value>Не удалось получить конфигурацию по умолчанию</value>
</data>
<data name="FailedImportedCustomServer" xml:space="preserve">
<value>Не удалось импортировать пользовательскую конфигурацию</value>
<value>Не удалось импортировать сервер пользовательской конфигурации</value>
</data>
<data name="FailedReadConfiguration" xml:space="preserve">
<value>Не удалось прочитать файл конфигурации</value>
@ -211,13 +211,13 @@
<value>Не удалось импортировать подписку</value>
</data>
<data name="MsgGetSubscriptionSuccessfully" xml:space="preserve">
<value>Содержимое подписки успешно получено</value>
<value>Содержимое подписки успешно импортировано</value>
</data>
<data name="MsgNoValidSubscription" xml:space="preserve">
<value>Нет установленных подписок</value>
</data>
<data name="MsgParsingSuccessfully" xml:space="preserve">
<value>Успешно обработано: {0}</value>
<value>Парсинг {0} прошел успешно</value>
</data>
<data name="MsgStartGettingSubscriptions" xml:space="preserve">
<value>Начинаю получать подписки</value>
@ -244,7 +244,7 @@
<value>Успешное обновление ядра! Перезапуск службы...</value>
</data>
<data name="NonvmessOrssProtocol" xml:space="preserve">
<value>Протокол не VMess и не Shadowsocks</value>
<value>Не является протоколом VMess или Shadowsocks</value>
</data>
<data name="NotFoundCore" xml:space="preserve">
<value>Файл ядра ({1}) не найден в папке {0}. Скачайте по адресу {2} и поместите его туда</value>
@ -253,7 +253,7 @@
<value>Сканирование завершено, не найден корректный QR код</value>
</data>
<data name="OperationFailed" xml:space="preserve">
<value>Операция не удалась, проверьте и повторите попытку</value>
<value>Операция безуспешна, проверьте и попробуйте ещё раз</value>
</data>
<data name="PleaseFillRemarks" xml:space="preserve">
<value>Введите примечания</value>
@ -268,7 +268,7 @@
<value>Сначала выберите сервер</value>
</data>
<data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>Дедупликация конфигураций завершена. Было: {0}, Стало: {1}.</value>
<value>Удаление дублей завершено. Старая: {0}, Новая: {1}</value>
</data>
<data name="RemoveServer" xml:space="preserve">
<value>Вы уверены, что хотите удалить сервер?</value>
@ -283,16 +283,16 @@
<value>Конфигурация выполнена успешно {0}</value>
</data>
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
<value>Пользовательская конфигурация успешно импортирована</value>
<value>Пользовательская конфигурация сервера успешно импортирована</value>
</data>
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
<value>Из буфера обмена импортировано конфигураций: {0}</value>
<value>{0} серверов импортировано из буфера обмена</value>
</data>
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
<value>Ссылка общего доступа успешно отсканирована и импортирована</value>
<value>Сканирование URL-адреса импорта прошло успешно</value>
</data>
<data name="TestMeOutput" xml:space="preserve">
<value>Задержка: {0} мс, {1}</value>
<value>Задержка текущего сервера: {0} мс, {1}</value>
</data>
<data name="OperationSuccess" xml:space="preserve">
<value>Операция успешна</value>
@ -334,7 +334,7 @@
<value>Введите корректный пользовательский DNS</value>
</data>
<data name="TransportPathTip1" xml:space="preserve">
<value>*Путь ws/http upgrade/xhttp</value>
<value>*WebSocket-путь</value>
</data>
<data name="TransportPathTip2" xml:space="preserve">
<value>*HTTP2-путь</value>
@ -349,7 +349,7 @@
<value>*http-хосты, разделённые запятыми (,)</value>
</data>
<data name="TransportRequestHostTip2" xml:space="preserve">
<value>*Хост ws/http upgrade/xhttp</value>
<value>*WebSocket-хост</value>
</data>
<data name="TransportRequestHostTip3" xml:space="preserve">
<value>*HTTP2-хосты, разделённые запятыми (,)</value>
@ -382,7 +382,7 @@
<value>Глобальная горячая клавиша {0} зарегистрирована успешно</value>
</data>
<data name="AllGroupServers" xml:space="preserve">
<value>Все</value>
<value>Все серверы</value>
</data>
<data name="FillServerAddressCustom" xml:space="preserve">
<value>Выберите файл конфигурации сервера для импорта</value>
@ -397,7 +397,7 @@
<value>Локальный</value>
</data>
<data name="MsgServerTitle" xml:space="preserve">
<value>Фильтр, нажмите Enter для выполнения</value>
<value>Фильтр серверов</value>
</data>
<data name="menuCheckUpdate" xml:space="preserve">
<value>Проверить обновления</value>
@ -409,19 +409,19 @@
<value>Выход</value>
</data>
<data name="menuGlobalHotkeySetting" xml:space="preserve">
<value>Настройка глобальных горячих клавиш</value>
<value>Глобальная настройка горячих клавиш</value>
</data>
<data name="menuHelp" xml:space="preserve">
<value>Помощь</value>
</data>
<data name="menuOptionSetting" xml:space="preserve">
<value>Настройки</value>
<value>Настройка параметров</value>
</data>
<data name="menuPromotion" xml:space="preserve">
<value>Продвижение</value>
<value>Содействие</value>
</data>
<data name="menuReload" xml:space="preserve">
<value>Перезагрузить</value>
<value>Перезагрузка</value>
</data>
<data name="menuRoutingSetting" xml:space="preserve">
<value>Настройки маршрутизации</value>
@ -436,13 +436,13 @@
<value>Обновить текущую подписку без прокси</value>
</data>
<data name="menuSubGroupUpdateViaProxy" xml:space="preserve">
<value>Обновить текущую подписку через прокси</value>
<value>Обновить подписку через прокси</value>
</data>
<data name="menuSubscription" xml:space="preserve">
<value>Группа подписки</value>
</data>
<data name="menuSubSetting" xml:space="preserve">
<value>Настройки групп подписки</value>
<value>Настройки группы подписки</value>
</data>
<data name="menuSubUpdate" xml:space="preserve">
<value>Обновить подписку без прокси</value>
@ -472,46 +472,46 @@
<value>Язык (требуется перезапуск)</value>
</data>
<data name="menuAddServerViaClipboard" xml:space="preserve">
<value>Импорт ссылок общего доступа из буфера обмена</value>
<value>Импорт массива URL из буфера обмена</value>
</data>
<data name="menuAddServerViaScan" xml:space="preserve">
<value>Сканировать QR-код на экране</value>
<value>Сканировать QR-код с экрана</value>
</data>
<data name="menuCopyServer" xml:space="preserve">
<value>Клонировать выбранное</value>
<value>Клонировать выбранный сервер</value>
</data>
<data name="menuRemoveDuplicateServer" xml:space="preserve">
<value>Удалить дубликаты</value>
<value>Удалить дубликаты серверов</value>
</data>
<data name="menuRemoveServer" xml:space="preserve">
<value>Удалить выбранное</value>
<value>Удалить выбранные серверы</value>
</data>
<data name="menuSetDefaultServer" xml:space="preserve">
<value>Сделать активным</value>
<value>Установить как активный сервер</value>
</data>
<data name="menuClearServerStatistics" xml:space="preserve">
<value>Очистить статистику всех сервисов</value>
<value>Очистить всю статистику</value>
</data>
<data name="menuRealPingServer" xml:space="preserve">
<value>Тест реальной задержки</value>
<value>Тест на реальную задержку сервера</value>
</data>
<data name="menuSortServerResult" xml:space="preserve">
<value>Сортировка по результату теста</value>
<value>Сортировать по результату теста</value>
</data>
<data name="menuSpeedServer" xml:space="preserve">
<value>Тест скорости загрузки</value>
<value>Тест на скорость загрузки сервера</value>
</data>
<data name="menuTcpingServer" xml:space="preserve">
<value>Тест tcping</value>
<value>Тест задержки с tcping</value>
</data>
<data name="menuExport2ClientConfig" xml:space="preserve">
<value>Экспортировать выбранное как полную конфигурацию</value>
<value>Экспортировать выбранный сервер для клиента</value>
</data>
<data name="menuExport2ShareUrl" xml:space="preserve">
<value>Копировать ссылку общего доступа в буфер обмена</value>
<value>Экспорт URL-адресов общего доступа в буфер обмена</value>
</data>
<data name="menuAddCustomServer" xml:space="preserve">
<value>Добавить пользовательскую конфигурацию</value>
<value>Добавить сервер пользовательской конфигурации</value>
</data>
<data name="menuAddShadowsocksServer" xml:space="preserve">
<value>Добавить сервер [Shadowsocks]</value>
@ -556,13 +556,13 @@
<value>Поделиться</value>
</data>
<data name="LvEnabled" xml:space="preserve">
<value>Включить обновление</value>
<value>Включены обновления</value>
</data>
<data name="LvSort" xml:space="preserve">
<value>Сортировка</value>
</data>
<data name="LvUserAgent" xml:space="preserve">
<value>User-Agent</value>
<value>Заголовок User-Agent</value>
</data>
<data name="TbCancel" xml:space="preserve">
<value>Отмена</value>
@ -595,7 +595,7 @@
<value>UUID (id)</value>
</data>
<data name="TbNetwork" xml:space="preserve">
<value>Транспортный протокол (network)</value>
<value>Транспортный протокол сети</value>
</data>
<data name="TbPath" xml:space="preserve">
<value>Путь</value>
@ -604,13 +604,13 @@
<value>Порт</value>
</data>
<data name="TbRemarks" xml:space="preserve">
<value>Псевдоним (remarks)</value>
<value>Примечание</value>
</data>
<data name="TbRequestHost" xml:space="preserve">
<value>Камуфляжный домен (host)</value>
<value>Маскирующий домен (хост)</value>
</data>
<data name="TbSecurity" xml:space="preserve">
<value>Метод шифрования (security)</value>
<value>Метод шифрования</value>
</data>
<data name="TbSNI" xml:space="preserve">
<value>SNI</value>
@ -619,13 +619,13 @@
<value>TLS</value>
</data>
<data name="TipNetwork" xml:space="preserve">
<value>*По умолчанию TCP</value>
<value>*По-умолчанию TCP</value>
</data>
<data name="TbCoreType" xml:space="preserve">
<value>Ядро</value>
</data>
<data name="TbFlow5" xml:space="preserve">
<value>Управление потоком (Flow)</value>
<value>Поток</value>
</data>
<data name="TbGUID" xml:space="preserve">
<value>Генерировать</value>
@ -634,7 +634,7 @@
<value>Пароль</value>
</data>
<data name="TbId4" xml:space="preserve">
<value>Парольеобязательно)</value>
<value>Пароль(Необязательно)</value>
</data>
<data name="TbId5" xml:space="preserve">
<value>UUID(id)</value>
@ -643,10 +643,10 @@
<value>Шифрование</value>
</data>
<data name="TbSecurity4" xml:space="preserve">
<value>Пользовательеобязательно)</value>
<value>Пользователь(Необязательно)</value>
</data>
<data name="TbSecurity5" xml:space="preserve">
<value>Шифрование</value>
<value>Шифрования</value>
</data>
<data name="TbPreSocksPort" xml:space="preserve">
<value>Порт SOCKS</value>
@ -655,7 +655,7 @@
<value>* После установки этого значения служба SOCKS будет запущена с использованием Xray/sing-box(TUN) для обеспечения таких функций, как отображение скорости</value>
</data>
<data name="TbBrowse" xml:space="preserve">
<value>Обзор</value>
<value>Просмотр</value>
</data>
<data name="TbEdit" xml:space="preserve">
<value>Редактировать</value>
@ -667,7 +667,7 @@
<value>Разрешить подключения из локальной сети</value>
</data>
<data name="TbSettingsAutoHideStartup" xml:space="preserve">
<value>Автоматически скрывать при запуске</value>
<value>Автоскрытие при автозапуске</value>
</data>
<data name="TbSettingsAutoUpdateInterval" xml:space="preserve">
<value>Интервал автоматического обновления Geo в часах</value>
@ -676,7 +676,7 @@
<value>Ядро: базовые настройки</value>
</data>
<data name="TbCustomDnsRay" xml:space="preserve">
<value>Пользовательский DNS для V2ray</value>
<value>V2ray Custom DNS</value>
</data>
<data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Ядро: настройки KCP</value>
@ -700,10 +700,10 @@
<value>Исключение</value>
</data>
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Исключения: не использовать прокси для адресов, начинающихся с указанных. Разделяйте точкой с запятой (;)</value>
<value>Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;)</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Показывать скорость в реальном времени (требуется перезапуск)</value>
<value>Показывать скорость в реальном времени</value>
</data>
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
<value>Сохранить старые при удалении дублей</value>
@ -721,7 +721,7 @@
<value>Настройки v2rayN</value>
</data>
<data name="TbSettingsPass" xml:space="preserve">
<value>Пароль авторизации</value>
<value>Пароль аутентификации</value>
</data>
<data name="TbSettingsRemoteDNS" xml:space="preserve">
<value>Пользовательский DNS (если несколько, то делите запятыми (,))</value>
@ -736,10 +736,10 @@
<value>Смешанный порт</value>
</data>
<data name="TbSettingsStartBoot" xml:space="preserve">
<value>Запускать при старте системы</value>
<value>Автозапуск</value>
</data>
<data name="TbSettingsStatistics" xml:space="preserve">
<value>Включить статистику трафика (требуется перезапуск)</value>
<value>Включить статистику (требуется перезагрузка)</value>
</data>
<data name="TbSettingsSubConvert" xml:space="preserve">
<value>URL конвертации подписок</value>
@ -748,31 +748,31 @@
<value>Настройки системного прокси</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Лимит отображения серверов в контекстном меню трея</value>
<value>Лимит серверов в меню трея</value>
</data>
<data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>Включить UDP</value>
</data>
<data name="TbSettingsUser" xml:space="preserve">
<value>Пользователь авторизации</value>
<value>Имя пользователя (логин)</value>
</data>
<data name="TbClearSystemProxy" xml:space="preserve">
<value>Очистить системный прокси</value>
</data>
<data name="TbDisplayGUI" xml:space="preserve">
<value>Показать интерфейс</value>
<value>Показать GUI</value>
</data>
<data name="TbGlobalHotkeySetting" xml:space="preserve">
<value>Настройка глобальных горячих клавиш</value>
<value>Настройка горячих клавиш</value>
</data>
<data name="TbGlobalHotkeySettingTip" xml:space="preserve">
<value>Задайте, нажав нужную комбинацию клавиш; вступит в силу после перезапуска</value>
<value>Установите непосредственно, нажав на клавиатуру, вступит в силу после перезапуска</value>
</data>
<data name="TbNotChangeSystemProxy" xml:space="preserve">
<value>Не менять системный прокси</value>
<value>Не изменять системный прокси</value>
</data>
<data name="TbReset" xml:space="preserve">
<value>Сбросить</value>
<value>Обнулить</value>
</data>
<data name="TbSetSystemProxy" xml:space="preserve">
<value>Установить системный прокси</value>
@ -781,16 +781,16 @@
<value>Режим PAC</value>
</data>
<data name="menuShareServer" xml:space="preserve">
<value>Поделиться</value>
<value>Поделиться сервером</value>
</data>
<data name="menuRouting" xml:space="preserve">
<value>Маршрутизация</value>
</data>
<data name="NotRunAsAdmin" xml:space="preserve">
<value>Запущено без прав администратора</value>
<value>Пользователь</value>
</data>
<data name="RunAsAdmin" xml:space="preserve">
<value>Запущено от имени администратора</value>
<value>Администратор</value>
</data>
<data name="menuMoveBottom" xml:space="preserve">
<value>Спуститься вниз</value>
@ -808,13 +808,13 @@
<value>Фильтр, поддерживает regex</value>
</data>
<data name="menuWebsiteItem" xml:space="preserve">
<value>Веб-сайт {0}</value>
<value>{0} веб-сайт</value>
</data>
<data name="menuRoutingAdvancedAdd" xml:space="preserve">
<value>Добавить</value>
</data>
<data name="menuRoutingAdvancedImportRules" xml:space="preserve">
<value>Импортировать правила</value>
<value>Добавить расширенные правила</value>
</data>
<data name="menuRoutingAdvancedRemove" xml:space="preserve">
<value>Удалить выбранные</value>
@ -859,7 +859,7 @@
<value>Детальные настройки правил маршрутизации</value>
</data>
<data name="TbAutoSort" xml:space="preserve">
<value>Домен, IP и процесс автоматически сортируются при сохранении</value>
<value>Домен и IP автоматически сортируются при сохранении</value>
</data>
<data name="TbRuleobjectDoc" xml:space="preserve">
<value>Документация RuleObject</value>
@ -868,7 +868,7 @@
<value>Поддерживаются DNS-объекты, нажмите для просмотра документации</value>
</data>
<data name="SubUrlTips" xml:space="preserve">
<value>Для группы оставьте это поле пустым</value>
<value>Необязательное поле</value>
</data>
<data name="TipChangeRouting" xml:space="preserve">
<value>Настройки маршрутизации изменены</value>
@ -880,7 +880,7 @@
<value>Только маршрут</value>
</data>
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
<value>Не использовать прокси для локальных (интранет) адресов</value>
<value>Не используйте прокси-серверы для локальных (интранет) адресов</value>
</data>
<data name="menuMixedTestServer" xml:space="preserve">
<value>Тест задержки и скорости всех серверов (Ctrl+E)</value>
@ -895,13 +895,13 @@
<value>Не удалось запустить ядро, посмотрите логи</value>
</data>
<data name="LvFilter" xml:space="preserve">
<value>Фильтр по примечаниям (регулярные выражения)</value>
<value>Фильтр примечаний (Regex)</value>
</data>
<data name="TbDisplayLog" xml:space="preserve">
<value>Отображать журнал</value>
<value>Показать логи</value>
</data>
<data name="TbEnableTunAs" xml:space="preserve">
<value>Включить TUN</value>
<value>Режим VPN</value>
</data>
<data name="TbSettingsNewPort4LAN" xml:space="preserve">
<value>Новый порт для локальной сети</value>
@ -910,10 +910,10 @@
<value>Настройки режима TUN</value>
</data>
<data name="menuMoveToGroup" xml:space="preserve">
<value>Переместить в группу</value>
<value>Перейти в группу</value>
</data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>Включить сортировку перетаскиванием сервера (требуется перезапуск)</value>
<value>Включить сортировку перетаскиванием сервера (требуется перезагрузка)</value>
</data>
<data name="TbAutoRefresh" xml:space="preserve">
<value>Автообновление</value>
@ -922,10 +922,10 @@
<value>Пропустить тест</value>
</data>
<data name="menuEditServer" xml:space="preserve">
<value>Редактировать</value>
<value>Редактировать сервер</value>
</data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Двойной клик для активации конфигурации</value>
<value>Двойной клик чтобы сделать сервер активным</value>
</data>
<data name="SpeedtestingCompleted" xml:space="preserve">
<value>Тест завершен</value>
@ -940,16 +940,16 @@
<value>Параметр действует только для TCP/HTTP и WebSocket (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>
</data>
<data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>PAC-порт = +3; Xray API порт = +4; mihomo API порт = +5;</value>
<value>Pac порт = +3,Xray API порт = +4, mihomo API порт = +5</value>
</data>
<data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Установите с правами администратора; после запуска приложение получит права администратора</value>
<value>Установите это с правами администратора</value>
</data>
<data name="TbSettingsFontSize" xml:space="preserve">
<value>Размер шрифта</value>
@ -964,7 +964,7 @@
<value>Переместить вверх/вниз</value>
</data>
<data name="TbPublicKey" xml:space="preserve">
<value>Открытый ключ</value>
<value>PublicKey</value>
</data>
<data name="TbShortId" xml:space="preserve">
<value>ShortId</value>
@ -973,22 +973,22 @@
<value>SpiderX</value>
</data>
<data name="TbSettingsEnableHWA" xml:space="preserve">
<value>Включить аппаратное ускорение (требуется перезапуск)</value>
<value>Включить аппаратное ускорение (требуется перезагрузка)</value>
</data>
<data name="SpeedtestingWait" xml:space="preserve">
<value>Ожидание…</value>
<value>Ожидание тестирования…</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>Нажмите ESC для прекращения теста</value>
<value>нажмите ESC для отмены</value>
</data>
<data name="TipDisplayLog" xml:space="preserve">
<value>Отключите при нестабильном соединении</value>
<value>Отключите при аномальном разрыве соединения</value>
</data>
<data name="MsgSkipSubscriptionUpdate" xml:space="preserve">
<value>Обновления не включены — подписка пропущена</value>
</data>
<data name="menuRebootAsAdmin" xml:space="preserve">
<value>Перезапустить от имени администратора</value>
<value>Перезагрузить как администратор</value>
</data>
<data name="LvMoreUrl" xml:space="preserve">
<value>Дополнительные URL через запятую, конвертация подписки недоступна</value>
@ -1003,7 +1003,7 @@
<value>Включить запись логов в файл</value>
</data>
<data name="LvConvertTarget" xml:space="preserve">
<value>Целевой тип конвертации</value>
<value>Преобразовать тип цели</value>
</data>
<data name="LvConvertTargetTip" xml:space="preserve">
<value>Если преобразование не требуется, оставьте поле пустым</value>
@ -1012,7 +1012,7 @@
<value>Настройки DNS</value>
</data>
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>Пользовательский DNS для sing-box</value>
<value>sing-box Custom DNS</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
@ -1027,7 +1027,7 @@
<value>Протокол Mux для sing-box</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Процесс (Linux/Windows)</value>
<value>Process (Linux/Windows)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP-адрес или сеть CIDR</value>
@ -1042,13 +1042,13 @@
<value>Максимальная пропускная способность Hysteria (загрузка/отдача)</value>
</data>
<data name="TbSettingsUseSystemHosts" xml:space="preserve">
<value>Использовать системный файл hosts</value>
<value>Использовать системные узлы</value>
</data>
<data name="menuAddTuicServer" xml:space="preserve">
<value>Добавить сервер [TUIC]</value>
</data>
<data name="TbHeaderType8" xml:space="preserve">
<value>Управление перегрузками</value>
<value>Контроль перегрузок</value>
</data>
<data name="LvPrevProfile" xml:space="preserve">
<value>Примечания к предыдущему прокси</value>
@ -1084,7 +1084,7 @@
<value>Зарезервировано (2, 3, 4)</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Адрес (IPv4, IPv6)</value>
<value>Адрес (Ipv4,Ipv6)</value>
</data>
<data name="TbPath7" xml:space="preserve">
<value>Пароль obfs</value>
@ -1099,7 +1099,7 @@
<value>URL для быстрой проверки реальной задержки</value>
</data>
<data name="SpeedtestingStop" xml:space="preserve">
<value>Завершение тестирования...</value>
<value>Отмена тестирования...</value>
</data>
<data name="TransportRequestHostTip5" xml:space="preserve">
<value>* gRPC Authority (HTTP/2 псевдозаголовок :authority)</value>
@ -1117,7 +1117,7 @@
<value>Пользовательский набор правил для sing-box</value>
</data>
<data name="NeedRebootTips" xml:space="preserve">
<value>Операция успешна. Закройте приложение, нажав «Выход» в меню трея, и запустите его заново</value>
<value>Операция успешна. Перезапустите приложение</value>
</data>
<data name="menuOpenTheFileLocation" xml:space="preserve">
<value>Открыть место хранения</value>
@ -1138,10 +1138,10 @@
<value>Скорость загрузки</value>
</data>
<data name="TbSortingDownTraffic" xml:space="preserve">
<value>Загруженный трафик</value>
<value>Скачанный трафик</value>
</data>
<data name="TbSortingHost" xml:space="preserve">
<value>Хост</value>
<value>Узел</value>
</data>
<data name="TbSortingName" xml:space="preserve">
<value>Имя</value>
@ -1204,7 +1204,7 @@
<value>Стратегия домена по умолчанию для исходящих</value>
</data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>Основная ориентация макета (требуется перезапуск)</value>
<value>Основная ориентация макета (требуется перезагрузка)</value>
</data>
<data name="TbSettingsDomainDNSAddress" xml:space="preserve">
<value>Исходящий DNS адрес</value>
@ -1216,13 +1216,13 @@
<value>Экспорт ссылок в формате Base64 в буфер обмена</value>
</data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>Экспортировать полную конфигурацию в буфер обмена</value>
<value>Экспортировать выбранный сервер для полной конфигурации в буфер обмена</value>
</data>
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Показать или скрыть главное окно</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Порт SOCKS для пользовательской конфигурации</value>
<value>Пользовательская конфигурация порта SOCKS</value>
</data>
<data name="menuBackupAndRestore" xml:space="preserve">
<value>Резервное копирование и восстановление</value>
@ -1276,13 +1276,13 @@
<value>Источник файлов наборов правил sing-box (необязательно)</value>
</data>
<data name="UpgradeAppNotExistTip" xml:space="preserve">
<value>Приложение для обновления не найдено</value>
<value>Программы для обновления не существует</value>
</data>
<data name="TbSettingsRoutingRulesSource" xml:space="preserve">
<value>Источник правил маршрутизации (необязательно)</value>
<value>Источник правил маршрутизации</value>
</data>
<data name="menuRegionalPresets" xml:space="preserve">
<value>Настройка региональных пресетов</value>
<value>Региональные пресеты</value>
</data>
<data name="menuRegionalPresetsDefault" xml:space="preserve">
<value>По умолчанию (Китай)</value>
@ -1300,13 +1300,13 @@
<value>Сканировать QR-код с изображения</value>
</data>
<data name="InvalidUrlTip" xml:space="preserve">
<value>Неверный адрес (URL)</value>
<value>Неверный адрес (Url)</value>
</data>
<data name="InsecureUrlProtocol" xml:space="preserve">
<value>Не используйте небезопасный адрес подписки по протоколу HTTP</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Установите шрифт в систему, выберите или введите имя шрифта, перезапустите настройки</value>
<value>Установите шрифт в систему и перезапустите настройки</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>Вы уверены, что хотите выйти?</value>
@ -1324,16 +1324,16 @@
<value>*XHTTP-режим</value>
</data>
<data name="TransportExtraTip" xml:space="preserve">
<value>Дополнительный сырой JSON для XHTTP, формат: { XHTTP Object }</value>
<value>Дополнительный сырой JSON для XHTTP, формат: { XHTTP Object }</value>
</data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Сворачивать в трей при закрытии окна</value>
<value>Скрыть в трее при закрытии окна</value>
</data>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>Количество одновременно выполняемых тестов при многоэтапном тестировании</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Исключения: не использовать прокси для указанных адресов. Разделяйте запятой (,)</value>
<value>Исключение. Не используйте прокси-сервер для адресов с запятой (,)</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>Тип сниффинга</value>
@ -1345,7 +1345,7 @@
<value>socks: локальный порт, socks2: второй локальный порт, socks3: LAN порт</value>
</data>
<data name="TbSettingsTheme" xml:space="preserve">
<value>Тема</value>
<value>Темы</value>
</data>
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>Копировать команду прокси в буфер обмена</value>
@ -1393,10 +1393,10 @@
<value>Внутренний DNS</value>
</data>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>Стратегия разрешения прямых соединений</value>
<value>Direct Target Resolution Strategy</value>
</data>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>Стратегия разрешения прокси-соединений</value>
<value>Proxy Target Resolution Strategy</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Добавить стандартные записи hosts (DNS)</value>
@ -1429,7 +1429,7 @@
<value>Включён пользовательский DNS — настройки на этой странице не применяются</value>
</data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>При включении блокирует проверки доступности ECH и HTTP/3</value>
<value>Block ECH and HTTP/3 availability checks when enabled</value>
</data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>Пожалуйста, заполните корректный шаблон конфигурации</value>
@ -1462,127 +1462,127 @@
<value>Эта функция предназначена для продвинутых пользователей и особых случаев. После включения игнорируются базовые настройки ядра, DNS и маршрутизации. Вы должны самостоятельно корректно задать порт системного прокси, учёт трафика и другие связанные параметры — всё настраивается вручную.</value>
</data>
<data name="MsgStartParsingSubscription" xml:space="preserve">
<value>Начинается разбор и обработка содержимого подписки</value>
<value>Start parsing and processing subscription content</value>
</data>
<data name="TbSelectProfile" xml:space="preserve">
<value>Выбрать профиль</value>
<value>Select Profile</value>
</data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>По умолчанию применяется глобально, со встроенной фильтрацией FakeIP (только sing-box).</value>
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>Добавьте хотя бы одну конфигурацию</value>
<value>Please Add At Least One Configuration</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>Группа политик</value>
<value>Policy Group</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>Цепочка прокси</value>
<value>Proxy Chain</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>Наименьшая задержка</value>
<value>Lowest Latency</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>Случайный</value>
<value>Random</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>Циклический (Round Robin)</value>
<value>Round Robin</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>Наиболее стабильный</value>
<value>Most Stable</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>Тип группы политик</value>
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Добавить группу политик </value>
<value>Add Policy Group Configuration</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Добавить цепочку прокси</value>
<value>Add Proxy Chain Configuration</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Добавить дочернюю конфигурацию </value>
<value>Add Child Configuration</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Удалить дочернюю конфигурацию </value>
<value>Remove Child Configuration</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>Конфигурация 1: автодобавление из группы подписки</value>
<value>Configuration item 1, Auto add from subscription group</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Резервный (Fallback)</value>
<value>Fallback</value>
</data>
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
<value>Ядро «{0}» не поддерживает тип сети «{1}»</value>
<value>Core '{0}' does not support network type '{1}'</value>
</data>
<data name="MsgCoreNotSupportProtocolTransport" xml:space="preserve">
<value>Ядро «{0}» не поддерживает протокол «{1}» при транспорте «{2}»</value>
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'</value>
</data>
<data name="MsgCoreNotSupportProtocol" xml:space="preserve">
<value>Ядро «{0}» не поддерживает протокол «{1}»</value>
<value>Core '{0}' does not support protocol '{1}'</value>
</data>
<data name="MsgInvalidProperty" xml:space="preserve">
<value>Свойство {0} недопустимо, проверьте его</value>
<value>The {0} property is invalid, please check</value>
</data>
<data name="MsgNotSupportProtocol" xml:space="preserve">
<value>Протокол «{0}» не поддерживается</value>
<value>Not support protocol '{0}'</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>Если в системе нет функции трея, не включайте эту опцию</value>
<value>If the system does not have a tray function, please do not enable it</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>Можно задать отдельные правила для маршрутизации и DNS или выбрать «ALL» для применения к обоим</value>
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>Тип правила</value>
<value>Rule Type</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Разрешает домены DNS-серверов, требуется IP-адрес</value>
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Тест реальной задержки</value>
<value>Test real delay</value>
</data>
<data name="TbPolicyGroupSubChildTip" xml:space="preserve">
<value>Автодобавление отфильтрованных конфигураций из групп подписки</value>
<value>Auto add filtered configuration from subscription groups</value>
</data>
<data name="TbCertPinning" xml:space="preserve">
<value>Привязка сертификата</value>
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Привязанный сертификат (заполните любое из полей)
При указании сертификат будет привязан, а «Разрешить небезопасные» отключится.
<value>Pinned certificate (fill in either one)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
Получение сертификата может завершиться неудачей при использовании самоподписанного сертификата или при наличии ненадёжного / вредоносного ЦС в системе.</value>
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Получить сертификат</value>
<value>Fetch Certificate</value>
</data>
<data name="TbFetchCertChain" xml:space="preserve">
<value>Получить цепочку сертификатов</value>
<value>Fetch Certificate Chain</value>
</data>
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
<value>Укажите корректный домен</value>
<value>Please set a valid domain</value>
</data>
<data name="CertNotSet" xml:space="preserve">
<value>Сертификат не задан</value>
<value>Certificate not set</value>
</data>
<data name="CertSet" xml:space="preserve">
<value>Сертификат задан</value>
<value>Certificate set</value>
</data>
<data name="TbSettingsCustomSystemProxyPacPath" xml:space="preserve">
<value>Путь к пользовательскому PAC-файлу</value>
<value>Custom PAC file path</value>
</data>
<data name="TbSettingsCustomSystemProxyScriptPath" xml:space="preserve">
<value>Путь к скрипту системного прокси</value>
<value>Custom system proxy script file path</value>
</data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>Отображать в Dock на macOS (требуется перезапуск)</value>
<value>macOS displays this in the Dock (requires restart)</value>
</data>
<data name="menuServerList2" xml:space="preserve">
<value>Конфигурация 2: выбор и добавление из собственных</value>
<value>Configuration Item 2, Select and add from self-built</value>
</data>
<data name="TbEchConfigList" xml:space="preserve">
<value>EchConfigList</value>
@ -1591,111 +1591,81 @@
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>Полный сертификат (цепочка) в формате PEM</value>
<value>Full certificate (chain), PEM format</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>Отпечаток сертификата (SHA-256)</value>
<value>Certificate fingerprint (SHA-256)</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>Отдавать устаревшие записи (Serve Stale)</value>
<value>Serve Stale</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>Параллельные запросы</value>
<value>Parallel Query</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>По умолчанию используется только при разрешении имён в процессе маршрутизации</value>
<value>By default, invoked only during routing for resolution</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>По умолчанию используется только при разрешении имён в процессе маршрутизации; убедитесь, что удалённый сервер может достичь этого DNS</value>
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>Если не задано или «AsIs», используется системный DNS; иначе — встроенный DNS-модуль.</value>
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>Если не задано или «AsIs», разрешение DNS выполняется DNS удалённого сервера; иначе — встроенный DNS-модуль.</value>
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>Интервал смены портов (Port Hopping)</value>
<value>Port hopping interval</value>
</data>
<data name="menuServerListPreview" xml:space="preserve">
<value>Предпросмотр конфигурации</value>
<value>Configuration item preview</value>
</data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
<data name="MsgRoutingRuleOutboundNodeWarning" xml:space="preserve">
<value>Правило маршрутизации {0}, исходящий узел {1}, предупреждение: {2}</value>
<value>Routing rule {0} outbound node {1} warning: {2}</value>
</data>
<data name="MsgRoutingRuleOutboundNodeError" xml:space="preserve">
<value>Правило маршрутизации {0}, исходящий узел {1}, ошибка: {2}. Используется только прокси-узел.</value>
<value>Routing rule {0} outbound node {1} error: {2}. Fallback to proxy node only.</value>
</data>
<data name="MsgGroupCycleDependency" xml:space="preserve">
<value>Группа {0} имеет циклическую зависимость на дочерний узел {1}. Узел пропущен.</value>
<value>Group {0} has a cycle dependency on child node {1}. Skipping this node.</value>
</data>
<data name="MsgGroupChildNodeWarning" xml:space="preserve">
<value>Группа {0}: предупреждение дочернего узла {1}: {2}</value>
<value>Group {0} child node {1} warning: {2}</value>
</data>
<data name="MsgGroupChildNodeError" xml:space="preserve">
<value>Группа {0}: ошибка дочернего узла {1}: {2}. Узел пропущен.</value>
<value>Group {0} child node {1} error: {2}. Skipping this node.</value>
</data>
<data name="MsgGroupChildGroupNodeWarning" xml:space="preserve">
<value>Группа {0}: предупреждение дочернего узла группы {1}: {2}</value>
<value>Group {0} child group node {1} warning: {2}</value>
</data>
<data name="MsgGroupChildGroupNodeError" xml:space="preserve">
<value>Группа {0}: ошибка дочернего узла группы {1}: {2}. Узел пропущен.</value>
<value>Group {0} child group node {1} error: {2}. Skipping this node.</value>
</data>
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>У группы {0} нет допустимых дочерних узлов.</value>
<value>Group {0} has no valid child node.</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>У правила маршрутизации {0} пустой исходящий тег. Используется только прокси-узел.</value>
<value>Routing rule {0} has an empty outbound tag. Fallback to proxy node only.</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>Правило маршрутизации {0}, исходящий узел {1} не найден. Используется только прокси-узел.</value>
<value>Routing rule {0} outbound node {1} not found. Fallback to proxy node only.</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>Предыдущий прокси подписки {0} не найден. Пропущено.</value>
<value>Subscription previous proxy {0} not found. Skipping.</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Следующий прокси подписки {0} не найден. Пропущено.</value>
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>Сгенерировать группу политик</value>
<value>Generate Policy Group</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>Все конфигурации</value>
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Группировка по регионам</value>
</data>
<data name="menuEditCopy" xml:space="preserve">
<value>Скопировать</value>
</data>
<data name="menuEditSelectAll" xml:space="preserve">
<value>Выбрать все</value>
</data>
<data name="menuEditPaste" xml:space="preserve">
<value>Вставить</value>
</data>
<data name="menuEditFormat" xml:space="preserve">
<value>Форматировать</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP поверх TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Добавить сервер [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Небезопасная конкурентность (Insecure Concurrency)</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Имя пользователя</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>Политика маршрутизации ICMP</value>
</data>
<data name="TbLegacyProtect" xml:space="preserve">
<value>Устаревшая защита TUN (Legacy Protect)</value>
<value>Group by Region</value>
</data>
</root>

View file

@ -1320,6 +1320,9 @@
<data name="TbSettingsSendThrough" xml:space="preserve">
<value>本地出站地址 (SendThrough)</value>
</data>
<data name="TbSettingsSendThroughHint" xml:space="preserve">
<value>例如 192.168.1.10</value>
</data>
<data name="TbSettingsSendThroughTip" xml:space="preserve">
<value>仅对 Xray 生效,填写本机 IPv4留空则不设置。</value>
</data>
@ -1674,34 +1677,4 @@
<data name="menuGenRegionGroup" xml:space="preserve">
<value>按地区分组</value>
</data>
<data name="menuEditCopy" xml:space="preserve">
<value>复制</value>
</data>
<data name="menuEditSelectAll" xml:space="preserve">
<value>全选</value>
</data>
<data name="menuEditPaste" xml:space="preserve">
<value>粘贴</value>
</data>
<data name="menuEditFormat" xml:space="preserve">
<value>格式化</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>添加 [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>不安全并发</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>用户名</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP 路由策略</value>
</data>
<data name="TbLegacyProtect" xml:space="preserve">
<value>旧版 TUN 保护</value>
</data>
</root>

View file

@ -1665,34 +1665,4 @@
<data name="menuGenRegionGroup" xml:space="preserve">
<value>按區域分組</value>
</data>
<data name="menuEditCopy" xml:space="preserve">
<value>複製</value>
</data>
<data name="menuEditSelectAll" xml:space="preserve">
<value>全選</value>
</data>
<data name="menuEditPaste" xml:space="preserve">
<value>貼上</value>
</data>
<data name="menuEditFormat" xml:space="preserve">
<value>格式化</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>新增 [NaïveProxy] 節點</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>不安全的並行處理</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>使用者名稱</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP 路由策略</value>
</data>
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
</root>

View file

@ -61,8 +61,7 @@ public partial class CoreConfigSingboxService(CoreConfigContext context)
ret.Success = true;
ret.Data = ApplyFullConfigTemplate();
if (!context.AppConfig.TunModeItem.EnableLegacyProtect
&& context.TunProtectSsPort is > 0 and <= 65535)
if (context.TunProtectSsPort is > 0 and <= 65535)
{
var ssInbound = new
{

View file

@ -22,7 +22,7 @@ public partial class CoreConfigSingboxService
}
if (withSelector)
{
var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(baseTagName)).Select(n => n.tag).ToList();
var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList();
if (proxyTags.Count > 1)
{
proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName));
@ -112,7 +112,6 @@ public partial class CoreConfigSingboxService
outbound.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod)
? protocolExtra.SsMethod : Global.None;
outbound.password = _node.Password;
outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null;
if (_node.Network == nameof(ETransport.tcp) && _node.HeaderType == Global.TcpHeaderHttp)
{
@ -224,14 +223,13 @@ public partial class CoreConfigSingboxService
password = protocolExtra.SalamanderPass.TrimEx(),
};
}
int? upMbps = protocolExtra?.UpMbps is { } su and >= 0
outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0
? su
: _config.HysteriaItem.UpMbps;
int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0
outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0
? sd
: _config.HysteriaItem.UpMbps;
outbound.up_mbps = upMbps > 0 ? upMbps : null;
outbound.down_mbps = downMbps > 0 ? downMbps : null;
: _config.HysteriaItem.DownMbps;
var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null;
if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
{
@ -271,7 +269,7 @@ public partial class CoreConfigSingboxService
{
outbound.uuid = _node.Username;
outbound.password = _node.Password;
outbound.congestion_control = protocolExtra.CongestionControl;
outbound.congestion_control = _node.HeaderType;
break;
}
case EConfigType.Anytls:
@ -279,22 +277,6 @@ public partial class CoreConfigSingboxService
outbound.password = _node.Password;
break;
}
case EConfigType.Naive:
{
outbound.username = _node.Username;
outbound.password = _node.Password;
if (protocolExtra.NaiveQuic == true)
{
outbound.quic = true;
outbound.quic_congestion_control = protocolExtra.CongestionControl.NullIfEmpty();
}
if (protocolExtra.InsecureConcurrency > 0)
{
outbound.insecure_concurrency = protocolExtra.InsecureConcurrency;
}
outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null;
break;
}
}
FillOutboundTls(outbound);
@ -345,14 +327,6 @@ public partial class CoreConfigSingboxService
{
try
{
// The synthetic TUN relay outbound talks to the local Xray shadowsocks relay.
// Xray cannot terminate sing-box h2mux, so muxing here turns local relay traffic
// into sp.mux.sing-box.arpa pseudo-destinations and breaks DNS over TUN.
if (IsTunRelayProxyOutbound())
{
return;
}
var muxEnabled = _node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
if (muxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty())
{
@ -372,21 +346,6 @@ public partial class CoreConfigSingboxService
}
}
private bool IsTunRelayProxyOutbound()
{
if (!context.IsTunEnabled
|| _node.ConfigType != EConfigType.Shadowsocks
|| _node.Address != Global.Loopback
|| _node.Port != context.ProxyRelaySsPort
|| _node.Password != Global.None)
{
return false;
}
var protocolExtra = _node.GetProtocolExtra();
return protocolExtra.SsMethod == Global.None;
}
private void FillOutboundTls(Outbound4Sbox outbound)
{
try
@ -595,12 +554,7 @@ public partial class CoreConfigSingboxService
for (var i = 0; i < nodes.Count; i++)
{
var node = nodes[i];
var currentTag = $"{baseTagName}-{i + 1}-{node.Remarks}";
if (nodes.Count == 1)
{
currentTag = baseTagName;
}
var currentTag = $"{baseTagName}-{i + 1}";
if (node.ConfigType.IsGroupType())
{
@ -631,8 +585,8 @@ public partial class CoreConfigSingboxService
for (var i = 0; i < nodesReverse.Count; i++)
{
var node = nodesReverse[i];
var currentTag = i == 0 ? baseTagName : $"chain-{baseTagName}-{i}-{node.Remarks}";
var dialerProxyTag = i != nodesReverse.Count - 1 ? $"chain-{baseTagName}-{i + 1}-{nodesReverse[i + 1].Remarks}" : null;
var currentTag = i == 0 ? baseTagName : $"chain-{baseTagName}-{i}";
var dialerProxyTag = i != nodesReverse.Count - 1 ? $"chain-{baseTagName}-{i + 1}" : null;
if (node.ConfigType.IsGroupType())
{
var childProfiles = new CoreConfigSingboxService(context with { Node = node, }).BuildGroupProxyOutbounds(currentTag);
@ -773,12 +727,13 @@ public partial class CoreConfigSingboxService
}, null);
}
var idx = echConfig.IndexOf('+');
var queryServerName = idx > 0 ? echConfig[..idx] : null;
// NOTE: query_server_name, since sing-box 1.13.0
//var queryServerName = idx > 0 ? echConfig[..idx] : null;
var echDnsServer = idx > 0 ? echConfig[(idx + 1)..] : echConfig;
return (new Ech4Sbox()
{
enabled = true,
query_server_name = queryServerName,
query_server_name = null,
}, ParseDnsAddress(echDnsServer));
}
}

View file

@ -47,36 +47,6 @@ public partial class CoreConfigSingboxService
outbound = Global.DirectTag,
process_name = lstDirectExe
});
// ICMP Routing
var icmpRouting = _config.TunModeItem.IcmpRouting ?? "";
if (!Global.TunIcmpRoutingPolicies.Contains(icmpRouting))
{
icmpRouting = Global.TunIcmpRoutingPolicies.First();
}
if (icmpRouting == "direct")
{
_coreConfig.route.rules.Add(new()
{
network = ["icmp"],
outbound = Global.DirectTag,
});
}
else if (icmpRouting != "rule")
{
var rejectMethod = icmpRouting switch
{
"unreachable" => "default",
"drop" => "drop",
_ => "reply",
};
_coreConfig.route.rules.Add(new()
{
network = ["icmp"],
action = "reject",
method = rejectMethod,
});
}
}
if (_config.Inbound.First().SniffingEnabled)
@ -494,7 +464,7 @@ public partial class CoreConfigSingboxService
return Global.ProxyTag;
}
var tag = $"{node.IndexId}-{Global.ProxyTag}-{node.Remarks}";
var tag = $"{node.IndexId}-{Global.ProxyTag}";
if (_coreConfig.outbounds.Any(o => o.tag.StartsWith(tag))
|| (_coreConfig.endpoints != null && _coreConfig.endpoints.Any(e => e.tag.StartsWith(tag))))
{

View file

@ -15,10 +15,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
var ret = new RetResult();
try
{
if (!context.AppConfig.TunModeItem.EnableLegacyProtect
&& context.IsTunEnabled
&& context.TunProtectSsPort is > 0 and <= 65535
&& context.ProxyRelaySsPort is > 0 and <= 65535)
if (context.IsTunEnabled && context.TunProtectSsPort > 0 && context.ProxyRelaySsPort > 0)
{
return GenerateClientProxyRelayConfig();
}
@ -322,50 +319,24 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
SsMethod = Global.None,
});
const string protectTag = "tun-protect-ss";
foreach (var outbound in _coreConfig.outbounds
.Where(o => o.streamSettings?.sockopt?.dialerProxy?.IsNullOrEmpty() ?? true))
{
outbound.streamSettings ??= new();
outbound.streamSettings.sockopt ??= new();
outbound.streamSettings.sockopt.dialerProxy = protectTag;
outbound.streamSettings.sockopt.dialerProxy = "tun-protect-ss";
}
// ech protected
foreach (var outbound in _coreConfig.outbounds
.Where(outbound => outbound.streamSettings?.tlsSettings?.echConfigList?.IsNullOrEmpty() == false))
{
outbound.streamSettings!.tlsSettings!.echSockopt ??= new();
outbound.streamSettings.tlsSettings.echSockopt.dialerProxy = protectTag;
}
// xhttp download protected
foreach (var outbound in _coreConfig.outbounds
.Where(o => o.streamSettings?.xhttpSettings?.extra is not null))
{
var xhttpExtra = JsonUtils.ParseJson(JsonUtils.Serialize(outbound.streamSettings.xhttpSettings!.extra));
if (xhttpExtra is not JsonObject xhttpExtraObject
|| xhttpExtraObject["downloadSettings"] is not JsonObject downloadSettings)
{
continue;
}
// dialerProxy
var sockopt = downloadSettings["sockopt"] as JsonObject ?? new JsonObject();
sockopt["dialerProxy"] = protectTag;
downloadSettings["sockopt"] = sockopt;
// ech protected
if (downloadSettings["tlsSettings"] is JsonObject tlsSettings
&& tlsSettings["echConfigList"] is not null)
{
tlsSettings["echSockopt"] = new JsonObject
{
["dialerProxy"] = protectTag
};
}
outbound.streamSettings.xhttpSettings.extra = xhttpExtraObject;
outbound.streamSettings.tlsSettings.echSockopt.dialerProxy = "tun-protect-ss";
}
_coreConfig.outbounds.Add(new CoreConfigV2rayService(context with
{
Node = protectNode,
}).BuildProxyOutbound(protectTag));
}).BuildProxyOutbound("tun-protect-ss"));
_coreConfig.routing.rules ??= [];
var hasBalancer = _coreConfig.routing.balancers is { Count: > 0 };
@ -415,37 +386,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
var sendThrough = _config.CoreBasicItem.SendThrough?.TrimEx();
foreach (var outbound in _coreConfig.outbounds ?? [])
{
outbound.sendThrough = ShouldApplySendThrough(outbound, sendThrough) ? sendThrough : null;
outbound.sendThrough = sendThrough.IsNullOrEmpty() ? null : sendThrough;
}
}
private static bool ShouldApplySendThrough(Outbounds4Ray outbound, string? sendThrough)
{
if (sendThrough.IsNullOrEmpty())
{
return false;
}
if (outbound.protocol is "freedom" or "blackhole" or "dns" or "loopback")
{
return false;
}
if (outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == false)
{
return false;
}
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
?? outbound.settings?.vnext?.FirstOrDefault()?.address
?? outbound.settings?.address?.ToString()
?? string.Empty;
if (outboundAddress.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return !IPAddress.TryParse(outboundAddress, out var address) || !IPAddress.IsLoopback(address);
}
}

View file

@ -109,7 +109,9 @@ public partial class CoreConfigV2rayService
?? string.Empty;
if (!Utils.IsPrivateNetwork(outboundAddress))
{
FillDialerProxy(outbound, fullConfigTemplate.ProxyDetour);
outbound.streamSettings ??= new StreamSettings4Ray();
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
}
}
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));

View file

@ -51,7 +51,10 @@ public partial class CoreConfigV2rayService
}
foreach (var outbound in actOutboundWithTlsList)
{
FillDialerProxy(outbound, fragmentOutbound.tag);
outbound.streamSettings.sockopt = new()
{
dialerProxy = fragmentOutbound.tag
};
}
}
return proxyOutboundList;
@ -150,7 +153,6 @@ public partial class CoreConfigV2rayService
serversItem.password = _node.Password;
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod)
? protocolExtra.SsMethod : "none";
serversItem.uot = protocolExtra.Uot == true ? true : null;
serversItem.ota = false;
serversItem.level = 1;
@ -351,7 +353,11 @@ public partial class CoreConfigV2rayService
var host = _node.RequestHost.TrimEx();
var path = _node.Path.TrimEx();
var sni = _node.Sni.TrimEx();
var useragent = _config.CoreBasicItem.DefUserAgent ?? string.Empty;
var useragent = "";
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
{
useragent = Global.UserAgentTexts.GetValueOrDefault(_config.CoreBasicItem.DefUserAgent, _config.CoreBasicItem.DefUserAgent);
}
//if tls
if (_node.StreamSecurity == Global.StreamSecurity)
@ -434,10 +440,10 @@ public partial class CoreConfigV2rayService
kcpSettings.congestion = _config.KcpItem.Congestion;
kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize;
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
var kcpFinalmask = new Finalmask4Ray();
streamSettings.finalmask ??= new();
if (Global.KcpHeaderMaskMap.TryGetValue(_node.HeaderType, out var header))
{
kcpFinalmask.udp =
streamSettings.finalmask.udp =
[
new Mask4Ray
{
@ -446,24 +452,23 @@ public partial class CoreConfigV2rayService
}
];
}
kcpFinalmask.udp ??= [];
streamSettings.finalmask.udp ??= [];
if (path.IsNullOrEmpty())
{
kcpFinalmask.udp.Add(new Mask4Ray
streamSettings.finalmask.udp.Add(new Mask4Ray
{
type = "mkcp-original"
});
}
else
{
kcpFinalmask.udp.Add(new Mask4Ray
streamSettings.finalmask.udp.Add(new Mask4Ray
{
type = "mkcp-aes128gcm",
settings = new MaskSettings4Ray { password = path }
});
}
streamSettings.kcpSettings = kcpSettings;
streamSettings.finalmask = kcpFinalmask;
break;
//ws
case nameof(ETransport.ws):
@ -489,17 +494,13 @@ public partial class CoreConfigV2rayService
case nameof(ETransport.httpupgrade):
HttpupgradeSettings4Ray httpupgradeSettings = new();
if (host.IsNotEmpty())
{
httpupgradeSettings.host = host;
}
if (path.IsNotEmpty())
{
httpupgradeSettings.path = path;
}
if (useragent.IsNotEmpty())
if (host.IsNotEmpty())
{
httpupgradeSettings.headers.UserAgent = useragent;
httpupgradeSettings.host = host;
}
streamSettings.httpupgradeSettings = httpupgradeSettings;
@ -578,7 +579,6 @@ public partial class CoreConfigV2rayService
health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
permit_without_stream = _config.GrpcItem.PermitWithoutStream,
initial_windows_size = _config.GrpcItem.InitialWindowsSize,
user_agent = useragent.NullIfEmpty(),
};
streamSettings.grpcSettings = grpcSettings;
break;
@ -597,32 +597,28 @@ public partial class CoreConfigV2rayService
: (_config.HysteriaItem.HopInterval >= 5
? _config.HysteriaItem.HopInterval
: Global.Hysteria2DefaultHopInt).ToString();
var hy2Finalmask = new Finalmask4Ray();
var quicParams = new QuicParams4Ray();
HysteriaUdpHop4Ray? udpHop = null;
if (!ports.IsNullOrEmpty() &&
(ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
{
var udpHop = new UdpHop4Ray
udpHop = new HysteriaUdpHop4Ray
{
ports = ports.Replace(':', '-'),
port = ports.Replace(':', '-'),
interval = hopInterval,
};
quicParams.udpHop = udpHop;
}
if (upMbps > 0 || downMbps > 0)
streamSettings.hysteriaSettings = new()
{
quicParams.congestion = "brutal";
quicParams.brutalUp = upMbps > 0 ? $"{upMbps}mbps" : null;
quicParams.brutalDown = downMbps > 0 ? $"{downMbps}mbps" : null;
}
else
{
quicParams.congestion = "bbr";
}
hy2Finalmask.quicParams = quicParams;
version = 2,
auth = _node.Password,
up = upMbps > 0 ? $"{upMbps}mbps" : null,
down = downMbps > 0 ? $"{downMbps}mbps" : null,
udphop = udpHop,
};
if (!protocolExtra.SalamanderPass.IsNullOrEmpty())
{
hy2Finalmask.udp =
streamSettings.finalmask ??= new();
streamSettings.finalmask.udp =
[
new Mask4Ray
{
@ -631,12 +627,6 @@ public partial class CoreConfigV2rayService
}
];
}
streamSettings.hysteriaSettings = new()
{
version = 2,
auth = _node.Password,
};
streamSettings.finalmask = hy2Finalmask;
break;
default:
@ -653,11 +643,10 @@ public partial class CoreConfigV2rayService
//request Host
var request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
var useragentValue = Global.TcpHttpUserAgentTexts.GetValueOrDefault(useragent, useragent);
var arrHost = host.Split(',');
var host2 = string.Join(",".AppendQuotes(), arrHost);
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
request = request.Replace("$requestUserAgent$", $"{useragentValue.AppendQuotes()}");
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
//Path
var pathHttp = @"/";
if (path.IsNotEmpty())
@ -675,7 +664,7 @@ public partial class CoreConfigV2rayService
if (!_node.Finalmask.IsNullOrEmpty())
{
streamSettings.finalmask = JsonUtils.ParseJson(_node.Finalmask);
streamSettings.finalmask = JsonUtils.Deserialize<Finalmask4Ray>(_node.Finalmask);
}
}
catch (Exception ex)
@ -698,12 +687,7 @@ public partial class CoreConfigV2rayService
for (var i = 0; i < nodes.Count; i++)
{
var node = nodes[i];
var currentTag = $"{baseTagName}-{i + 1}-{node.Remarks}";
if (nodes.Count == 1)
{
currentTag = baseTagName;
}
var currentTag = $"{baseTagName}-{i + 1}";
if (node.ConfigType.IsGroupType())
{
@ -734,8 +718,8 @@ public partial class CoreConfigV2rayService
for (var i = 0; i < nodesReverse.Count; i++)
{
var node = nodesReverse[i];
var currentTag = i == 0 ? baseTagName : $"chain-{baseTagName}-{i}-{node.Remarks}";
var dialerProxyTag = i != nodesReverse.Count - 1 ? $"chain-{baseTagName}-{i + 1}-{nodesReverse[i + 1].Remarks}" : null;
var currentTag = i == 0 ? baseTagName : $"chain-{baseTagName}-{i}";
var dialerProxyTag = i != nodesReverse.Count - 1 ? $"chain-{baseTagName}-{i + 1}" : null;
if (node.ConfigType.IsGroupType())
{
var childProfiles = new CoreConfigV2rayService(context with { Node = node, }).BuildGroupProxyOutbounds(currentTag);
@ -745,7 +729,10 @@ public partial class CoreConfigV2rayService
childProfiles.Where(n => n?.streamSettings?.sockopt?.dialerProxy?.IsNullOrEmpty() ?? true);
foreach (var chainEndNode in chainEndNodes)
{
FillDialerProxy(chainEndNode, dialerProxyTag);
chainEndNode.streamSettings.sockopt = new()
{
dialerProxy = dialerProxyTag
};
}
}
if (i != 0)
@ -753,10 +740,12 @@ public partial class CoreConfigV2rayService
var chainStartNodes = childProfiles.Where(n => n.tag.StartsWith(currentTag)).ToList();
if (chainStartNodes.Count == 1)
{
var firstChainTag = chainStartNodes.First().tag;
foreach (var existedChainEndNode in resultOutbounds.Where(n => n.streamSettings?.sockopt?.dialerProxy == currentTag))
{
FillDialerProxy(existedChainEndNode, firstChainTag);
existedChainEndNode.streamSettings.sockopt = new()
{
dialerProxy = chainStartNodes.First().tag
};
}
}
else if (chainStartNodes.Count > 1)
@ -779,8 +768,12 @@ public partial class CoreConfigV2rayService
var nextTag = k + 1 < existedChainNodesClone.Count
? existedChainNodesClone[k + 1].tag
: chainStartNode.tag;
FillDialerProxy(existedChainNode,
previousDialerProxyTag == currentTag ? chainStartNode.tag : nextTag);
existedChainNode.streamSettings.sockopt = new()
{
dialerProxy = (previousDialerProxyTag == currentTag)
? chainStartNode.tag
: nextTag
};
resultOutbounds.Add(existedChainNode);
}
j++;
@ -796,32 +789,14 @@ public partial class CoreConfigV2rayService
if (!dialerProxyTag.IsNullOrEmpty())
{
FillDialerProxy(outbound, dialerProxyTag);
outbound.streamSettings.sockopt = new()
{
dialerProxy = dialerProxyTag
};
}
resultOutbounds.Add(outbound);
}
return resultOutbounds;
}
private static void FillDialerProxy(Outbounds4Ray outbound, string dialerProxyTag)
{
outbound.streamSettings ??= new();
outbound.streamSettings.sockopt ??= new();
outbound.streamSettings.sockopt.dialerProxy = dialerProxyTag;
// xhttp download dialer proxy
if (outbound?.streamSettings?.xhttpSettings?.extra is not null)
{
var xhttpExtra = JsonUtils.ParseJson(JsonUtils.Serialize(outbound.streamSettings.xhttpSettings!.extra));
if (xhttpExtra is JsonObject xhttpExtraObject
&& xhttpExtraObject["downloadSettings"] is JsonObject downloadSettings)
{
var sockopt = downloadSettings["sockopt"] as JsonObject ?? new JsonObject();
sockopt["dialerProxy"] = dialerProxyTag;
downloadSettings["sockopt"] = sockopt;
outbound.streamSettings.xhttpSettings.extra = xhttpExtraObject;
}
}
}
}

View file

@ -164,7 +164,7 @@ public partial class CoreConfigV2rayService
return Global.ProxyTag;
}
var tag = $"{node.IndexId}-{Global.ProxyTag}-{node.Remarks}";
var tag = $"{node.IndexId}-{Global.ProxyTag}";
if (_coreConfig.outbounds.Any(p => p.tag.StartsWith(tag)))
{
return tag;

View file

@ -61,18 +61,6 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public int WgMtu { get; set; }
[Reactive]
public bool Uot { get; set; }
[Reactive]
public string CongestionControl { get; set; }
[Reactive]
public int? InsecureConcurrency { get; set; }
[Reactive]
public bool NaiveQuic { get; set; }
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
@ -135,10 +123,6 @@ public class AddServerViewModel : MyReactiveObject
WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty;
WgReserved = protocolExtra?.WgReserved ?? string.Empty;
WgMtu = protocolExtra?.WgMtu ?? 1280;
Uot = protocolExtra?.Uot ?? false;
CongestionControl = protocolExtra?.CongestionControl ?? string.Empty;
InsecureConcurrency = protocolExtra?.InsecureConcurrency > 0 ? protocolExtra.InsecureConcurrency : null;
NaiveQuic = protocolExtra?.NaiveQuic ?? false;
}
private async Task SaveServerAsync()
@ -201,10 +185,6 @@ public class AddServerViewModel : MyReactiveObject
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
WgReserved = WgReserved.NullIfEmpty(),
WgMtu = WgMtu >= 576 ? WgMtu : null,
Uot = Uot ? true : null,
CongestionControl = CongestionControl.NullIfEmpty(),
InsecureConcurrency = InsecureConcurrency > 0 ? InsecureConcurrency : null,
NaiveQuic = NaiveQuic ? true : null,
});
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)

View file

@ -18,7 +18,6 @@ public class MainWindowViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddAnytlsServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddNaiveServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddPolicyGroupServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddProxyChainServerCmd { get; }
@ -118,10 +117,6 @@ public class MainWindowViewModel : MyReactiveObject
{
await AddServerAsync(EConfigType.Anytls);
});
AddNaiveServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerAsync(EConfigType.Naive);
});
AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerAsync(EConfigType.Custom);

View file

@ -96,8 +96,6 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public string TunStack { get; set; }
[Reactive] public int TunMtu { get; set; }
[Reactive] public bool TunEnableIPv6Address { get; set; }
[Reactive] public string TunIcmpRouting { get; set; }
[Reactive] public bool TunEnableLegacyProtect { get; set; }
#endregion Tun mode
@ -222,8 +220,6 @@ public class OptionSettingViewModel : MyReactiveObject
TunStack = _config.TunModeItem.Stack;
TunMtu = _config.TunModeItem.Mtu;
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
TunIcmpRouting = _config.TunModeItem.IcmpRouting;
TunEnableLegacyProtect = _config.TunModeItem.EnableLegacyProtect;
#endregion Tun mode
@ -389,8 +385,6 @@ public class OptionSettingViewModel : MyReactiveObject
_config.TunModeItem.Stack = TunStack;
_config.TunModeItem.Mtu = TunMtu;
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
_config.TunModeItem.IcmpRouting = TunIcmpRouting;
_config.TunModeItem.EnableLegacyProtect = TunEnableLegacyProtect;
//coreType
await SaveCoreType();

View file

@ -709,22 +709,18 @@ public class ProfilesViewModel : MyReactiveObject
public async Task ServerSpeedtest(ESpeedActionType actionType)
{
List<ProfileItem>? lstSelected;
if (actionType is ESpeedActionType.Mixedtest or ESpeedActionType.FastRealping)
if (actionType == ESpeedActionType.Mixedtest)
{
if (actionType == ESpeedActionType.FastRealping)
SelectedProfiles = ProfileItems;
}
else if (actionType == ESpeedActionType.FastRealping)
{
SelectedProfiles = ProfileItems;
actionType = ESpeedActionType.Realping;
}
lstSelected = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(ProfileItems?.OrderBy(t => t.Sort)));
}
else
{
lstSelected = await GetProfileItems(false);
}
if (lstSelected is null || lstSelected.Count <= 0)
var lstSelected = await GetProfileItems(false);
if (lstSelected == null)
{
return;
}

View file

@ -490,7 +490,6 @@ public class StatusBarViewModel : MyReactiveObject
}
}
}
await ConfigHandler.SaveConfig(_config);
AppEvents.ReloadRequested.Publish();
}

View file

@ -1,189 +0,0 @@
using System.Runtime.InteropServices;
using SkiaSharp;
namespace v2rayN.Desktop.Common;
public partial class QRCodeAvaloniaUtils
{
public static byte[]? CaptureScreen()
{
if (!Utils.IsWindows())
{
return null;
}
try
{
return CaptureScreenWindows();
}
catch (Exception ex)
{
Logging.SaveLog("CaptureScreen", ex);
return null;
}
}
private static byte[]? CaptureScreenWindows()
{
var hdcScreen = IntPtr.Zero;
var hdcMemory = IntPtr.Zero;
var hBitmap = IntPtr.Zero;
try
{
var workArea = new RECT();
SystemParametersInfo(SPI_GETWORKAREA, 0, ref workArea, 0);
var left = workArea.Left;
var top = workArea.Top;
var width = workArea.Right - workArea.Left;
var height = workArea.Bottom - workArea.Top;
if (width <= 0 || height <= 0)
{
left = 0;
top = 0;
width = GetSystemMetrics(0);
height = GetSystemMetrics(1);
}
hdcScreen = GetDC(IntPtr.Zero);
if (hdcScreen == IntPtr.Zero)
{
return null;
}
hdcMemory = CreateCompatibleDC(hdcScreen);
hBitmap = CreateCompatibleBitmap(hdcScreen, width, height);
if (hBitmap == IntPtr.Zero)
{
return null;
}
SelectObject(hdcMemory, hBitmap);
const int SRCCOPY = 0x00CC0020;
BitBlt(hdcMemory, 0, 0, width, height, hdcScreen, left, top, SRCCOPY);
var bmi = new BITMAPINFO
{
biSize = Marshal.SizeOf(typeof(BITMAPINFO)),
biWidth = width,
biHeight = -height,
biPlanes = 1,
biBitCount = 32,
biCompression = 0
};
var imageSize = width * height * 4;
var imageData = new byte[imageSize];
var scanLines = GetDIBits(hdcScreen, hBitmap, 0, (uint)height, imageData, ref bmi, 0);
if (scanLines == 0)
{
return null;
}
using var bitmap = new SKBitmap(width, height, SKColorType.Bgra8888, SKAlphaType.Premul);
Marshal.Copy(imageData, 0, bitmap.GetPixels(), imageSize);
using var image = SKImage.FromBitmap(bitmap);
using var encoded = image.Encode(SKEncodedImageFormat.Png, 100);
return encoded.ToArray();
}
catch (Exception ex)
{
Logging.SaveLog("CaptureScreenWindows", ex);
return null;
}
finally
{
if (hBitmap != IntPtr.Zero)
{
DeleteObject(hBitmap);
}
if (hdcMemory != IntPtr.Zero)
{
DeleteDC(hdcMemory);
}
if (hdcScreen != IntPtr.Zero)
{
ReleaseDC(IntPtr.Zero, hdcScreen);
}
}
}
#region Win32 API
[LibraryImport("user32.dll")]
private static partial IntPtr GetDC(IntPtr hwnd);
[LibraryImport("user32.dll")]
private static partial int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[LibraryImport("gdi32.dll")]
private static partial IntPtr CreateCompatibleDC(IntPtr hdc);
[LibraryImport("gdi32.dll")]
private static partial IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[LibraryImport("gdi32.dll")]
private static partial IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
[LibraryImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
[LibraryImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool DeleteObject(IntPtr hObject);
[LibraryImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool DeleteDC(IntPtr hdc);
[LibraryImport("gdi32.dll")]
private static partial int GetDIBits(IntPtr hdc, IntPtr hbmp, uint uStartScan, uint cScanLines,
byte[] lpvBits, ref BITMAPINFO lpbmi, uint uUsage);
[LibraryImport("user32.dll")]
private static partial int GetSystemMetrics(int nIndex);
[LibraryImport("user32.dll", EntryPoint = "SystemParametersInfoW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool SystemParametersInfo(int uiAction, int uiParam, ref RECT pvParam, int fWinIni);
private const int SPI_GETWORKAREA = 0x0030;
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFO
{
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
}
#endregion Win32 API
}

View file

@ -5,7 +5,6 @@
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"
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuServers}"
Width="900"
@ -198,19 +197,6 @@
Width="300"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleSwitch
x:Name="togUotEnabled3"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
@ -497,7 +483,7 @@
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType8}" />
<ComboBox
x:Name="cmbCongestionControl8"
x:Name="cmbHeaderType8"
Grid.Row="3"
Grid.Column="1"
Width="200"
@ -594,93 +580,12 @@
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtId11"
x:Name="txtId10"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
<Grid
x:Name="gridNaive"
Grid.Row="2"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUsername}" />
<TextBox
x:Name="txtId12"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtSecurity12"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="QUIC" />
<StackPanel
Grid.Row="3"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleSwitch
x:Name="togNaiveQuic12"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<ComboBox
x:Name="cmbCongestionControl12"
Width="200"
Margin="{StaticResource Margin4}"
PlaceholderText="{x:Static resx:ResUI.TbHeaderType8}" />
</StackPanel>
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbInsecureConcurrency}" />
<TextBox
x:Name="txtInsecureConcurrency12"
Grid.Row="4"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleSwitch
x:Name="togUotEnabled12"
Grid.Row="5"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</Grid>
<Separator
x:Name="sepa2"
@ -753,13 +658,16 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TransportExtraTip}" />
<views:JsonEditor
<TextBox
x:Name="txtExtra"
Width="400"
MinHeight="100"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
VerticalAlignment="Center"
Classes="TextArea"
MinLines="6"
TextWrapping="Wrap" />
</StackPanel>
</Flyout>
</Button.Flyout>
@ -841,13 +749,13 @@
<Button.Flyout>
<Flyout>
<StackPanel>
<views:JsonEditor
<TextBox
x:Name="txtFinalmask"
Width="400"
MinHeight="100"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
AcceptsReturn="True"
Classes="TextArea"
TextWrapping="NoWrap" />
</StackPanel>
</Flyout>
</Button.Flyout>

View file

@ -80,7 +80,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
cmbFingerprint.SelectedValue = string.Empty;
gridFinalmask.IsVisible = false;
cmbCongestionControl8.ItemsSource = Global.TuicCongestionControls;
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
break;
case EConfigType.WireGuard:
@ -94,28 +94,10 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.Anytls:
gridAnytls.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbCoreType.IsEnabled = false;
gridFinalmask.IsVisible = false;
break;
case EConfigType.Naive:
gridNaive.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbCoreType.IsEnabled = false;
gridFinalmask.IsVisible = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
cmbAlpn.IsEnabled = false;
cmbAlpn.SelectedValue = string.Empty;
cmbAllowInsecure.IsEnabled = false;
cmbAllowInsecure.SelectedValue = string.Empty;
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
break;
}
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
@ -140,7 +122,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.Shadowsocks:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
break;
@ -175,7 +156,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.WireGuard:
@ -187,17 +168,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
break;
case EConfigType.Anytls:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables);
break;
case EConfigType.Naive:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.cmbCongestionControl12.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);

View file

@ -3,7 +3,6 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:v2rayN.Desktop.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
@ -400,7 +399,12 @@
BorderBrush="Gray"
BorderThickness="1"
Header="HTTP/SOCKS">
<local:JsonEditor Name="txtnormalDNSCompatible" VerticalAlignment="Stretch" />
<TextBox
Name="txtnormalDNSCompatible"
VerticalAlignment="Stretch"
Classes="TextArea"
MinLines="10"
TextWrapping="Wrap" />
</HeaderedContentControl>
</DockPanel>
</TabItem>
@ -469,7 +473,12 @@
BorderBrush="Gray"
BorderThickness="1"
Header="HTTP/SOCKS">
<local:JsonEditor Name="txtnormalDNS2Compatible" VerticalAlignment="Stretch" />
<TextBox
Name="txtnormalDNS2Compatible"
VerticalAlignment="Stretch"
Classes="TextArea"
MinLines="10"
TextWrapping="Wrap" />
</HeaderedContentControl>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
@ -479,7 +488,12 @@
BorderBrush="Gray"
BorderThickness="1"
Header="{x:Static resx:ResUI.TbSettingsTunMode}">
<local:JsonEditor Name="txttunDNS2Compatible" VerticalAlignment="Stretch" />
<TextBox
Name="txttunDNS2Compatible"
VerticalAlignment="Stretch"
Classes="TextArea"
MinLines="10"
TextWrapping="Wrap" />
</HeaderedContentControl>
</Grid>

View file

@ -5,7 +5,6 @@
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"
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuFullConfigTemplate}"
Width="900"
@ -95,7 +94,12 @@
BorderBrush="Gray"
BorderThickness="1"
Header="xray config template json">
<views:JsonEditor x:Name="rayFullConfigTemplate" VerticalAlignment="Stretch" />
<TextBox
x:Name="rayFullConfigTemplate"
VerticalAlignment="Stretch"
Classes="TextArea"
MinLines="10"
TextWrapping="Wrap" />
</HeaderedContentControl>
</DockPanel>
</TabItem>
@ -162,7 +166,12 @@
BorderBrush="Gray"
BorderThickness="1"
Header="sing-box config template json">
<views:JsonEditor x:Name="sbFullConfigTemplate" VerticalAlignment="Stretch" />
<TextBox
x:Name="sbFullConfigTemplate"
VerticalAlignment="Stretch"
Classes="TextArea"
MinLines="10"
TextWrapping="Wrap" />
</HeaderedContentControl>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
@ -172,7 +181,12 @@
BorderBrush="Gray"
BorderThickness="1"
Header="sing-box tun config template json">
<views:JsonEditor x:Name="sbFullTunConfigTemplate" VerticalAlignment="Stretch" />
<TextBox
x:Name="sbFullTunConfigTemplate"
VerticalAlignment="Stretch"
Classes="TextArea"
MinLines="10"
TextWrapping="Wrap" />
</HeaderedContentControl>
</Grid>
</DockPanel>

View file

@ -1,23 +0,0 @@
<UserControl
x:Class="v2rayN.Desktop.Views.JsonEditor"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ae="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib">
<ae:TextEditor
Name="Editor"
FontFamily="Cascadia Code,Consolas,Monospace"
FontSize="14"
ShowLineNumbers="True">
<ae:TextEditor.ContextMenu>
<ContextMenu>
<MenuItem Click="FormatJson_Click" Header="{x:Static resx:ResUI.menuEditFormat}" />
<Separator />
<MenuItem Click="Copy_Click" Header="{x:Static resx:ResUI.menuEditCopy}" />
<MenuItem Click="Paste_Click" Header="{x:Static resx:ResUI.menuEditPaste}" />
<MenuItem Click="SelectAll_Click" Header="{x:Static resx:ResUI.menuEditSelectAll}" />
</ContextMenu>
</ae:TextEditor.ContextMenu>
</ae:TextEditor>
</UserControl>

View file

@ -1,98 +0,0 @@
using System.Text.Json;
using System.Xml;
using AvaloniaEdit.Highlighting;
using AvaloniaEdit.Highlighting.Xshd;
namespace v2rayN.Desktop.Views;
public partial class JsonEditor : UserControl
{
private static readonly JsonSerializerOptions SIndentedOptions = new() { WriteIndented = true };
private static readonly Lazy<IHighlightingDefinition> SHighlightingDark =
new(() => BuildHighlighting(dark: true), isThreadSafe: true);
private static readonly Lazy<IHighlightingDefinition> SHighlightingLight =
new(() => BuildHighlighting(dark: false), isThreadSafe: true);
public static readonly StyledProperty<string> TextProperty =
AvaloniaProperty.Register<JsonEditor, string>(nameof(Text), defaultValue: string.Empty);
public string Text
{
get => GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public JsonEditor()
{
InitializeComponent();
var isDark = Application.Current?.ActualThemeVariant != ThemeVariant.Light;
Editor.SyntaxHighlighting = isDark ? SHighlightingDark.Value : SHighlightingLight.Value;
Editor.TextArea.TextView.Options.EnableHyperlinks = false;
Editor.TextChanged += (_, _) =>
{
if (Text != Editor.Text)
{
SetCurrentValue(TextProperty, Editor.Text);
}
};
this.GetObservable(TextProperty).Subscribe(text =>
{
if (Editor.Text != text)
{
Editor.Text = text ?? string.Empty;
}
});
}
private static IHighlightingDefinition BuildHighlighting(bool dark)
{
var keyColor = dark ? "#9CDCFE" : "#0451A5";
var strColor = dark ? "#CE9178" : "#A31515";
var numColor = dark ? "#B5CEA8" : "#098658";
var kwColor = dark ? "#569CD6" : "#0000FF";
var xshd = $"""
<?xml version="1.0"?>
<SyntaxDefinition name="JSON" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
<Color name="Key" foreground="{keyColor}" />
<Color name="String" foreground="{strColor}" />
<Color name="Number" foreground="{numColor}" />
<Color name="Keyword" foreground="{kwColor}" fontWeight="bold" />
<RuleSet>
<Rule color="Key">"([^"\\]|\\.)*"(?=\s*:)</Rule>
<Rule color="String">"([^"\\]|\\.)*"</Rule>
<Rule color="Number">-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?</Rule>
<Keywords color="Keyword">
<Word>true</Word>
<Word>false</Word>
<Word>null</Word>
</Keywords>
</RuleSet>
</SyntaxDefinition>
""";
using var reader = XmlReader.Create(new StringReader(xshd));
return HighlightingLoader.Load(reader, HighlightingManager.Instance);
}
private void FormatJson_Click(object? sender, RoutedEventArgs e)
{
try
{
var obj = JsonUtils.ParseJson(Editor.Text);
Editor.Text = JsonUtils.Serialize(obj, SIndentedOptions);
}
catch
{
// ignored
}
}
private void Copy_Click(object? sender, RoutedEventArgs e) => Editor.Copy();
private void Paste_Click(object? sender, RoutedEventArgs e) => Editor.Paste();
private void SelectAll_Click(object? sender, RoutedEventArgs e) => Editor.SelectAll();
}

View file

@ -50,7 +50,6 @@
<Separator />
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
<MenuItem x:Name="menuAddNaiveServer" Header="{x:Static resx:ResUI.menuAddNaiveServer}" />
</MenuItem>
<MenuItem Header="{x:Static resx:ResUI.menuSubscription}">

View file

@ -72,7 +72,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
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.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables);
@ -162,8 +161,8 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
else
{
Title = $"{Utils.GetVersion()}";
menuAddServerViaScan.IsVisible = false;
}
menuAddServerViaScan.IsVisible = false;
if (_config.UiItem.AutoHideStartup && Utils.IsWindows())
{
@ -336,17 +335,17 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
public async Task ScanScreenTaskAsync()
{
ShowHideWindow(false);
//ShowHideWindow(false);
await Task.Delay(200);
NoticeManager.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)");
await Task.CompletedTask;
//if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
//{
// //var bytes = QRCodeHelper.CaptureScreen(desktop);
// //await ViewModel?.ScanScreenResult(bytes);
//}
var bytes = QRCodeAvaloniaUtils.CaptureScreen();
if (bytes != null && ViewModel != null)
{
await ViewModel.ScanScreenResult(bytes);
}
ShowHideWindow(true);
//ShowHideWindow(true);
}
private async Task ScanImageTaskAsync()

View file

@ -79,8 +79,8 @@
<MenuItem
x:Name="menuMsgViewSelectAll"
Click="menuMsgViewSelectAll_Click"
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}"
InputGesture="Ctrl+A" />
InputGesture="Ctrl+A"
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" />
<MenuItem
x:Name="menuMsgViewCopy"
Click="menuMsgViewCopy_Click"

View file

@ -338,7 +338,7 @@
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
Watermark="0.0.0.0" />
Watermark="{x:Static resx:ResUI.TbSettingsSendThroughHint}" />
<TextBlock
Grid.Row="21"
Grid.Column="2"
@ -789,7 +789,7 @@
Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto,Auto"
DockPanel.Dock="Top"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="2"
@ -845,20 +845,6 @@
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
<ComboBox
x:Name="cmbIcmpRoutingPolicy"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
@ -872,18 +858,6 @@
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbLegacyProtect}" />
<ToggleSwitch
x:Name="togEnableLegacyProtect"
Grid.Row="8"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</Grid>
</TabItem>

View file

@ -34,7 +34,6 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs;
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbCoreType1.ItemsSource = Global.CoreTypes;
cmbCoreType2.ItemsSource = Global.CoreTypes;
@ -116,8 +115,6 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
@ -6,7 +6,6 @@
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<AssemblyName>v2rayN</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>

View file

@ -32,8 +32,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Action", "GitHub Act
..\.github\workflows\winget-publish.yml = ..\.github\workflows\winget-publish.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLib.Tests", "ServiceLib.Tests\ServiceLib.Tests.csproj", "{E0B6C5C7-ED48-42EB-947A-877779E9F555}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -60,10 +58,6 @@ Global
{CB3DE54F-3A26-AE02-1299-311132C32156}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Release|Any CPU.Build.0 = Release|Any CPU
{E0B6C5C7-ED48-42EB-947A-877779E9F555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0B6C5C7-ED48-42EB-947A-877779E9F555}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0B6C5C7-ED48-42EB-947A-877779E9F555}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0B6C5C7-ED48-42EB-947A-877779E9F555}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -5,7 +5,7 @@ using Microsoft.Win32;
namespace v2rayN.Common;
internal static partial class WindowsUtils
internal static class WindowsUtils
{
private static readonly string _tag = "WindowsUtils";
@ -39,8 +39,8 @@ internal static partial class WindowsUtils
}
}
[LibraryImport("dwmapi.dll")]
public static partial int DwmSetWindowAttribute(nint hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
[DllImport("dwmapi.dll")]
public static extern int DwmSetWindowAttribute(nint hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
public static ImageSource IconToImageSource(Icon icon)
{

View file

@ -1,6 +1,6 @@
namespace v2rayN.Manager;
public sealed partial class HotkeyManager
public sealed class HotkeyManager
{
private static readonly Lazy<HotkeyManager> _instance = new(() => new());
public static HotkeyManager Instance = _instance.Value;
@ -165,13 +165,11 @@ public sealed partial class HotkeyManager
}
}
[LibraryImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool RegisterHotKey(nint hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool RegisterHotKey(nint hWnd, int id, int fsModifiers, int vlc);
[LibraryImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool UnregisterHotKey(nint hWnd, int id);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterHotKey(nint hWnd, int id);
[Flags]
private enum KeyModifiers

View file

@ -277,20 +277,6 @@
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleButton
x:Name="togUotEnabled3"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
@ -660,7 +646,7 @@
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHeaderType8}" />
<ComboBox
x:Name="cmbCongestionControl8"
x:Name="cmbHeaderType8"
Grid.Row="3"
Grid.Column="1"
Width="200"
@ -786,115 +772,13 @@
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtId11"
x:Name="txtId10"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
<Grid
x:Name="gridNaive"
Grid.Row="2"
Visibility="Hidden">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<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.TbUsername}" />
<TextBox
x:Name="txtId12"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtSecurity12"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="QUIC" />
<StackPanel
Grid.Row="3"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleButton
x:Name="togNaiveQuic12"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<ComboBox
x:Name="cmbCongestionControl12"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Right"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbHeaderType8}"
Style="{StaticResource DefComboBox}" />
</StackPanel>
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbInsecureConcurrency}" />
<TextBox
x:Name="txtInsecureConcurrency12"
Grid.Row="4"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleButton
x:Name="togUotEnabled12"
Grid.Row="5"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</Grid>
<Separator
x:Name="sepa2"

View file

@ -75,7 +75,7 @@ public partial class AddServerWindow
cmbFingerprint.Text = string.Empty;
gridFinalmask.Visibility = Visibility.Collapsed;
cmbCongestionControl8.ItemsSource = Global.TuicCongestionControls;
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
break;
case EConfigType.WireGuard:
@ -89,28 +89,10 @@ public partial class AddServerWindow
case EConfigType.Anytls:
gridAnytls.Visibility = Visibility.Visible;
sepa2.Visibility = Visibility.Collapsed;
gridTransport.Visibility = Visibility.Collapsed;
cmbCoreType.IsEnabled = false;
lstStreamSecurity.Add(Global.StreamSecurityReality);
gridFinalmask.Visibility = Visibility.Collapsed;
break;
case EConfigType.Naive:
gridNaive.Visibility = Visibility.Visible;
sepa2.Visibility = Visibility.Collapsed;
gridTransport.Visibility = Visibility.Collapsed;
cmbCoreType.IsEnabled = false;
gridFinalmask.Visibility = Visibility.Collapsed;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.Text = string.Empty;
cmbAlpn.IsEnabled = false;
cmbAlpn.Text = string.Empty;
cmbAllowInsecure.IsEnabled = false;
cmbAllowInsecure.Text = string.Empty;
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
break;
}
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
@ -135,7 +117,6 @@ public partial class AddServerWindow
case EConfigType.Shadowsocks:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
break;
@ -170,7 +151,7 @@ public partial class AddServerWindow
case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
break;
case EConfigType.WireGuard:
@ -182,16 +163,7 @@ public partial class AddServerWindow
break;
case EConfigType.Anytls:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables);
break;
case EConfigType.Naive:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);

View file

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

View file

@ -71,7 +71,6 @@ public partial class MainWindow
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.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables);

View file

@ -406,7 +406,7 @@
Width="200"
Margin="{StaticResource Margin8}"
Style="{StaticResource DefTextBox}"
materialDesign:HintAssist.Hint="0.0.0.0" />
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbSettingsSendThroughHint}" />
<TextBlock
Grid.Row="21"
Grid.Column="2"
@ -1033,7 +1033,6 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@ -1101,22 +1100,6 @@
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
<ComboBox
x:Name="cmbIcmpRoutingPolicy"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
@ -1130,20 +1113,6 @@
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbLegacyProtect}" />
<ToggleButton
x:Name="togEnableLegacyProtect"
Grid.Row="8"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
</Grid>
</TabItem>

View file

@ -31,7 +31,6 @@ public partial class OptionSettingWindow
cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs;
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbCoreType1.ItemsSource = Global.CoreTypes;
cmbCoreType2.ItemsSource = Global.CoreTypes;
@ -121,8 +120,6 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.Text).DisposeWith(disposables);

View file

@ -8,7 +8,6 @@
<ApplicationIcon>Resources\v2rayN.ico</ApplicationIcon>
<ApplicationManifest>app.manifest</ApplicationManifest>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>