mirror of
https://github.com/2dust/v2rayN.git
synced 2026-10-11 15:54:59 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1883178b5f | |||
| 1cde521d75 | |||
| 2407d44f4f | |||
| fbf822d74c | |||
| 4869a6cbba | |||
| b501efbc45 | |||
| 5ea8ae6420 | |||
| fa9d4a4769 | |||
| 771ef3192a |
@@ -676,6 +676,15 @@ EOF
|
|||||||
find "$stage/opt/v2rayN" -type f -exec chmod 0644 {} +
|
find "$stage/opt/v2rayN" -type f -exec chmod 0644 {} +
|
||||||
[[ -f "$stage/opt/v2rayN/v2rayN" ]] && chmod 0755 "$stage/opt/v2rayN/v2rayN" || true
|
[[ -f "$stage/opt/v2rayN/v2rayN" ]] && chmod 0755 "$stage/opt/v2rayN/v2rayN" || true
|
||||||
|
|
||||||
|
# Core binaries must be executable before installation: ordinary users cannot
|
||||||
|
# chmod the root-owned files installed by dpkg.
|
||||||
|
local core
|
||||||
|
for core in xray/xray sing_box/sing-box mihomo/mihomo; do
|
||||||
|
if [[ -f "$stage/opt/v2rayN/bin/$core" ]]; then
|
||||||
|
chmod 0755 "$stage/opt/v2rayN/bin/$core"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
deb_out="$OUTPUT_DIR/v2rayn_${VERSION}_${deb_arch}.deb"
|
deb_out="$OUTPUT_DIR/v2rayn_${VERSION}_${deb_arch}.deb"
|
||||||
dpkg-deb --root-owner-group --build "$stage" "$deb_out"
|
dpkg-deb --root-owner-group --build "$stage" "$deb_out"
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ internal class UpgradeApp
|
|||||||
var thisAppOldFile = $"{Utils.GetExePath()}.tmp";
|
var thisAppOldFile = $"{Utils.GetExePath()}.tmp";
|
||||||
File.Delete(thisAppOldFile);
|
File.Delete(thisAppOldFile);
|
||||||
var splitKey = "/";
|
var splitKey = "/";
|
||||||
|
var allowedBaseDir = Utils.StartupPath();
|
||||||
|
var pathComparison = Utils.GetPathComparison();
|
||||||
|
|
||||||
using var archive = ZipFile.OpenRead(fileName);
|
using var archive = ZipFile.OpenRead(fileName);
|
||||||
foreach (var entry in archive.Entries)
|
foreach (var entry in archive.Entries)
|
||||||
@@ -65,16 +67,21 @@ internal class UpgradeApp
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fullName = string.Join(splitKey, lst[1..lst.Length]);
|
var fullName = string.Join(splitKey, lst[1..lst.Length]);
|
||||||
|
var entryOutputPath = Utils.GetPath(fullName);
|
||||||
|
if (!Utils.IsPathUnderDirectory(allowedBaseDir, entryOutputPath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{Resx.Resource.FailedUpgrade} blocked potential path traversal: {entry.FullName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(Utils.GetExePath(), entryOutputPath, pathComparison))
|
||||||
{
|
{
|
||||||
File.Move(Utils.GetExePath(), thisAppOldFile);
|
File.Move(Utils.GetExePath(), thisAppOldFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
var entryOutputPath = Utils.GetPath(fullName);
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
||||||
//In the bin folder, if the file already exists, it will be skipped
|
//In the bin folder, if the file already exists, it will be skipped
|
||||||
if (fullName.StartsWith("bin") && File.Exists(entryOutputPath))
|
if (fullName.StartsWith("bin", pathComparison) && File.Exists(entryOutputPath))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,28 @@ internal class Utils
|
|||||||
return Path.Combine(startupPath, fileName);
|
return Path.Combine(startupPath, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsPathUnderDirectory(string baseDir, string targetPath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(baseDir) || string.IsNullOrWhiteSpace(targetPath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseFull = Path.GetFullPath(baseDir);
|
||||||
|
var targetFull = Path.GetFullPath(targetPath);
|
||||||
|
|
||||||
|
baseFull = baseFull.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
|
return targetFull.StartsWith(baseFull, GetPathComparison());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringComparison GetPathComparison()
|
||||||
|
{
|
||||||
|
return OperatingSystem.IsWindows()
|
||||||
|
? StringComparison.OrdinalIgnoreCase
|
||||||
|
: StringComparison.Ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
public static string V2rayN => "v2rayN";
|
public static string V2rayN => "v2rayN";
|
||||||
|
|
||||||
public static void StartV2RayN()
|
public static void StartV2RayN()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.25.5</Version>
|
<Version>7.25.6</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<PackageVersion Include="sqlite-net-e" Version="1.11.285" />
|
<PackageVersion Include="sqlite-net-e" Version="1.11.285" />
|
||||||
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.4.1" />
|
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.4.1" />
|
||||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||||
<PackageVersion Include="TUnit" Version="1.72.16" />
|
<PackageVersion Include="TUnit" Version="1.73.19" />
|
||||||
<PackageVersion Include="TUnit.Assertions.Should" Version="1.72.16-beta" />
|
<PackageVersion Include="TUnit.Assertions.Should" Version="1.72.16-beta" />
|
||||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||||
<PackageVersion Include="YamlDotNet" Version="18.1.0" />
|
<PackageVersion Include="YamlDotNet" Version="18.1.0" />
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
namespace ServiceLib.Tests.Common;
|
||||||
|
|
||||||
|
public class UnixFileModeTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task AlreadyExecutableSystemBinary_ShouldNotRequireOwnership()
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /bin/sh is executable but owned by root on the Unix CI runners.
|
||||||
|
// A regular user must not need chmod permission just to launch it.
|
||||||
|
var mode = File.GetUnixFileMode("/bin/sh");
|
||||||
|
await Utils.SetUnixFileMode("/bin/sh").Should().BeTrue();
|
||||||
|
await File.GetUnixFileMode("/bin/sh").Should().BeEqualTo(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task NonExecutableOwnedFile_ShouldGainExecuteBitsWithoutLosingOtherPermissions()
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = Path.GetTempFileName();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead;
|
||||||
|
File.SetUnixFileMode(path, mode);
|
||||||
|
|
||||||
|
await Utils.SetUnixFileMode(path).Should().BeTrue();
|
||||||
|
await File.GetUnixFileMode(path).Should().BeEqualTo(mode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -587,13 +587,11 @@ public class CoreConfigV2rayServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Arguments(true)]
|
public async Task GenerateClientConfigContent_Tun_ShouldSkipIPv6RouteWithoutGlobalIPv6()
|
||||||
[Arguments(false)]
|
|
||||||
public async Task GenerateClientConfigContent_Tun_ShouldSkipIPv6RouteWithoutGlobalIPv6(bool enableIPv6Address)
|
|
||||||
{
|
{
|
||||||
// A host without a global IPv6 address has no IPv6 traffic that could bypass the tunnel,
|
// A host without a global IPv6 address has no IPv6 traffic that could bypass the tunnel,
|
||||||
// while ::/0 would pull IPv6 attempts into a tunnel they cannot leave.
|
// while ::/0 would pull IPv6 attempts into a tunnel they cannot leave.
|
||||||
var config = CoreConfigTestFactory.CreateConfigWithTun(ECoreType.Xray, enableIPv6Address);
|
var config = CoreConfigTestFactory.CreateConfigWithTun(ECoreType.Xray, enableIPv6Address: false);
|
||||||
CoreConfigTestFactory.BindAppManagerConfig(config);
|
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||||
|
|
||||||
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main");
|
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main");
|
||||||
@@ -611,6 +609,26 @@ public class CoreConfigV2rayServiceTests
|
|||||||
await ipv6Routes.Should().BeEmpty();
|
await ipv6Routes.Should().BeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task GenerateClientConfigContent_Tun_ShouldRouteIPv6WhenEnabledWithoutGlobalIPv6()
|
||||||
|
{
|
||||||
|
// Hosts behind NAT66 hold only ULA addresses, so EnableIPv6Address must still route ::/0.
|
||||||
|
var config = CoreConfigTestFactory.CreateConfigWithTun(ECoreType.Xray, enableIPv6Address: true);
|
||||||
|
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||||
|
|
||||||
|
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main");
|
||||||
|
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.Xray, hasGlobalIPv6Address: false);
|
||||||
|
|
||||||
|
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
|
||||||
|
|
||||||
|
await result.Success.Should().BeTrue();
|
||||||
|
var cfg = JsonUtils.Deserialize<V2rayConfig>(result.Data!.ToString())!;
|
||||||
|
var tunInbound = cfg.inbounds.FirstOrDefault(i => i.protocol == "tun");
|
||||||
|
|
||||||
|
await tunInbound.Should().NotBeNull();
|
||||||
|
await tunInbound!.settings.autoSystemRoutingTable.Should().Contain("::/0");
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task GenerateClientConfigContent_TunRouteExcludeAddress_ShouldSkipIPv6RangesWithoutGlobalIPv6()
|
public async Task GenerateClientConfigContent_TunRouteExcludeAddress_ShouldSkipIPv6RangesWithoutGlobalIPv6()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
namespace ServiceLib.Tests.Services;
|
||||||
|
|
||||||
|
public class SpeedtestServiceBatchTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
[Arguments(0)]
|
||||||
|
[Arguments(1000)]
|
||||||
|
public async Task EmptySelection_ShouldReturnNoBatches(int pageSize)
|
||||||
|
{
|
||||||
|
await GetBatches([], pageSize).Count.Should().BeEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Arguments(0)]
|
||||||
|
[Arguments(-1)]
|
||||||
|
public async Task NonPositivePageSize_ShouldKeepEveryEligibleProfile(int pageSize)
|
||||||
|
{
|
||||||
|
var profiles = CreateProfiles();
|
||||||
|
var batches = GetBatches(profiles, pageSize);
|
||||||
|
|
||||||
|
await batches.Count.Should().BeEqualTo(profiles.Count);
|
||||||
|
await batches.SelectMany(batch => batch).Count().Should().BeEqualTo(profiles.Count);
|
||||||
|
foreach (var profile in profiles)
|
||||||
|
{
|
||||||
|
await batches.SelectMany(batch => batch).Count(item => ReferenceEquals(item, profile)).Should().BeEqualTo(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task NormalBatches_ShouldRespectPageSizeAndKeepCoreTypesSeparate()
|
||||||
|
{
|
||||||
|
var profiles = CreateProfiles();
|
||||||
|
var batches = GetBatches(profiles, 2);
|
||||||
|
|
||||||
|
await batches.Count.Should().BeEqualTo(3);
|
||||||
|
await batches.SelectMany(batch => batch).Count().Should().BeEqualTo(profiles.Count);
|
||||||
|
foreach (var batch in batches)
|
||||||
|
{
|
||||||
|
await (batch.Count > 0 && batch.Count <= 2).Should().BeTrue();
|
||||||
|
await batch.Select(item => item.CoreType).Distinct().Count().Should().BeEqualTo(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ServerTestItem> CreateProfiles() =>
|
||||||
|
[
|
||||||
|
new() { CoreType = ECoreType.Xray },
|
||||||
|
new() { CoreType = ECoreType.Xray },
|
||||||
|
new() { CoreType = ECoreType.Xray },
|
||||||
|
new() { CoreType = ECoreType.sing_box },
|
||||||
|
new() { CoreType = ECoreType.sing_box }
|
||||||
|
];
|
||||||
|
|
||||||
|
private static List<List<ServerTestItem>> GetBatches(List<ServerTestItem> profiles, int pageSize)
|
||||||
|
{
|
||||||
|
var service = new SpeedtestService(new Config { SpeedTestItem = new() }, _ => Task.CompletedTask);
|
||||||
|
var method = typeof(SpeedtestService).GetMethod("GetTestBatchItem", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||||||
|
return (List<List<ServerTestItem>>)method.Invoke(service, [profiles, pageSize])!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1441,7 +1441,11 @@ public class Utils
|
|||||||
if (File.Exists(fileName))
|
if (File.Exists(fileName))
|
||||||
{
|
{
|
||||||
var currentMode = File.GetUnixFileMode(fileName);
|
var currentMode = File.GetUnixFileMode(fileName);
|
||||||
File.SetUnixFileMode(fileName, currentMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute);
|
var executableMode = currentMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute;
|
||||||
|
if (currentMode != executableMode)
|
||||||
|
{
|
||||||
|
File.SetUnixFileMode(fileName, executableMode);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,40 +300,6 @@ AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
|
|||||||
5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
|
5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
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
|
|
||||||
hNQ+IIX3Sj0rnP0qCglN6oH4EZw=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
|
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
|
||||||
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
|
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
|
||||||
@@ -732,20 +698,6 @@ LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
|
|||||||
4uJEvlz36hz1
|
4uJEvlz36hz1
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD
|
|
||||||
VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
|
|
||||||
A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
|
|
||||||
WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
|
|
||||||
IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
|
|
||||||
AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G
|
|
||||||
jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2
|
|
||||||
4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
|
|
||||||
BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7
|
|
||||||
VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm
|
|
||||||
ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
|
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
|
||||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||||
@@ -805,38 +757,6 @@ HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/
|
|||||||
bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
|
bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw
|
|
||||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
|
||||||
MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
|
||||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
|
||||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA
|
|
||||||
A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt
|
|
||||||
nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY
|
|
||||||
6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu
|
|
||||||
MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k
|
|
||||||
RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg
|
|
||||||
f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV
|
|
||||||
+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo
|
|
||||||
dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW
|
|
||||||
Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa
|
|
||||||
G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq
|
|
||||||
gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID
|
|
||||||
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
|
||||||
FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H
|
|
||||||
vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8
|
|
||||||
0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC
|
|
||||||
B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u
|
|
||||||
NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg
|
|
||||||
yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev
|
|
||||||
HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6
|
|
||||||
xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR
|
|
||||||
TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg
|
|
||||||
JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV
|
|
||||||
7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl
|
|
||||||
6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL
|
MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL
|
||||||
BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ
|
BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ
|
||||||
@@ -1189,31 +1109,6 @@ AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB
|
|||||||
RA+GsCyRxj3qrg+E
|
RA+GsCyRxj3qrg+E
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG
|
|
||||||
EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3
|
|
||||||
MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl
|
|
||||||
cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR
|
|
||||||
dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB
|
|
||||||
pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM
|
|
||||||
b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm
|
|
||||||
aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz
|
|
||||||
IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
|
||||||
MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT
|
|
||||||
lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz
|
|
||||||
AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5
|
|
||||||
VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG
|
|
||||||
ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2
|
|
||||||
BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG
|
|
||||||
AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M
|
|
||||||
U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh
|
|
||||||
bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C
|
|
||||||
+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
|
|
||||||
bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F
|
|
||||||
uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
|
|
||||||
XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt
|
MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt
|
||||||
MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg
|
MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg
|
||||||
|
|||||||
@@ -67,12 +67,10 @@ public partial class CoreConfigV2rayService
|
|||||||
|
|
||||||
var address = _config.TunModeItem.IPv4Address.NullIfEmpty() ?? Global.TunIPv4Address.First();
|
var address = _config.TunModeItem.IPv4Address.NullIfEmpty() ?? Global.TunIPv4Address.First();
|
||||||
tunInbound.settings.gateway = [address];
|
tunInbound.settings.gateway = [address];
|
||||||
// Route both families into the tunnel regardless of EnableIPv6Address. That option only
|
// Without ::/0, IPv6 bypasses the tunnel. Skip it only when the host has no global IPv6
|
||||||
// controls whether the interface gets an IPv6 address; leaving ::/0 out of the routing
|
// address and EnableIPv6Address is off; NAT66 hosts have only ULA addresses.
|
||||||
// table makes IPv6 follow the system default route and bypass the tunnel entirely.
|
var routeIPv6 = _config.TunModeItem.EnableIPv6Address || context.HasGlobalIPv6Address;
|
||||||
// A host without a global IPv6 address is the exception: it has nothing to leak,
|
tunInbound.settings.autoSystemRoutingTable = routeIPv6
|
||||||
// and IPv6 sent into the tunnel would have no way back out.
|
|
||||||
tunInbound.settings.autoSystemRoutingTable = context.HasGlobalIPv6Address
|
|
||||||
? ["0.0.0.0/0", "::/0"]
|
? ["0.0.0.0/0", "::/0"]
|
||||||
: ["0.0.0.0/0"];
|
: ["0.0.0.0/0"];
|
||||||
if (_config.TunModeItem.EnableIPv6Address == true)
|
if (_config.TunModeItem.EnableIPv6Address == true)
|
||||||
@@ -108,7 +106,7 @@ public partial class CoreConfigV2rayService
|
|||||||
.Where(x => x != null).ToList();
|
.Where(x => x != null).ToList();
|
||||||
|
|
||||||
var includeList = new List<IPNetwork2> { wholeInternet };
|
var includeList = new List<IPNetwork2> { wholeInternet };
|
||||||
var includeListV6 = context.HasGlobalIPv6Address
|
var includeListV6 = routeIPv6
|
||||||
? new List<IPNetwork2> { wholeInternetV6 }
|
? new List<IPNetwork2> { wholeInternetV6 }
|
||||||
: new List<IPNetwork2>();
|
: new List<IPNetwork2>();
|
||||||
|
|
||||||
|
|||||||
@@ -587,6 +587,12 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
||||||
{
|
{
|
||||||
List<List<ServerTestItem>> lstTest = [];
|
List<List<ServerTestItem>> lstTest = [];
|
||||||
|
if (lstSelected.Count == 0)
|
||||||
|
{
|
||||||
|
return lstTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageSize = Math.Max(pageSize, 1);
|
||||||
var lst1 = lstSelected.Where(t => t.CoreType == ECoreType.Xray).ToList();
|
var lst1 = lstSelected.Where(t => t.CoreType == ECoreType.Xray).ToList();
|
||||||
var lst2 = lstSelected.Where(t => t.CoreType == ECoreType.sing_box).ToList();
|
var lst2 = lstSelected.Where(t => t.CoreType == ECoreType.sing_box).ToList();
|
||||||
|
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ public partial class MainWindowViewModel : MyReactiveObject
|
|||||||
public async Task AddServerViaImageAsync()
|
public async Task AddServerViaImageAsync()
|
||||||
{
|
{
|
||||||
var imageFileName = await BrowseImageFileInteraction.HandleSafe(RxVoid.Default);
|
var imageFileName = await BrowseImageFileInteraction.HandleSafe(RxVoid.Default);
|
||||||
await AddScanResultAsync(imageFileName);
|
await ScanImageResult(imageFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ScanImageResult(string fileName)
|
public async Task ScanImageResult(string fileName)
|
||||||
|
|||||||
@@ -219,7 +219,8 @@ public partial class StatusBarViewModel : MyReactiveObject
|
|||||||
await ConfigHandler.InitBuiltinRouting(_config);
|
await ConfigHandler.InitBuiltinRouting(_config);
|
||||||
await RefreshRoutingsMenu();
|
await RefreshRoutingsMenu();
|
||||||
await InboundDisplayStatus();
|
await InboundDisplayStatus();
|
||||||
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true);
|
// The view refreshes its icon on initialization; interaction handlers are not registered yet.
|
||||||
|
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, false);
|
||||||
|
|
||||||
BlRouting = true;
|
BlRouting = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel Grid.IsSharedSizeScope="True">
|
||||||
<Border
|
<Border
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -32,8 +32,14 @@
|
|||||||
|
|
||||||
<Grid Margin="{StaticResource Margin4}" RowDefinitions="Auto,Auto,Auto,Auto">
|
<Grid Margin="{StaticResource Margin4}" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" MinWidth="300" />
|
<ColumnDefinition
|
||||||
<ColumnDefinition Width="Auto" MinWidth="200" />
|
Width="Auto"
|
||||||
|
MinWidth="300"
|
||||||
|
SharedSizeGroup="BackupLabel" />
|
||||||
|
<ColumnDefinition
|
||||||
|
Width="Auto"
|
||||||
|
MinWidth="200"
|
||||||
|
SharedSizeGroup="BackupButton" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
@@ -82,8 +88,14 @@
|
|||||||
Theme="{DynamicResource CardBorder}">
|
Theme="{DynamicResource CardBorder}">
|
||||||
<Grid Margin="{StaticResource Margin4}" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
<Grid Margin="{StaticResource Margin4}" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" MinWidth="300" />
|
<ColumnDefinition
|
||||||
<ColumnDefinition Width="Auto" MinWidth="200" />
|
Width="Auto"
|
||||||
|
MinWidth="300"
|
||||||
|
SharedSizeGroup="BackupLabel" />
|
||||||
|
<ColumnDefinition
|
||||||
|
Width="Auto"
|
||||||
|
MinWidth="200"
|
||||||
|
SharedSizeGroup="BackupButton" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
|||||||
@@ -75,22 +75,23 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
IsChecked="{Binding IsSelected}" />
|
IsChecked="{Binding IsSelected}" />
|
||||||
|
|
||||||
<StackPanel
|
<DockPanel
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource MarginLr4}"
|
||||||
IsVisible="{Binding ShowCheckPreRelease}"
|
IsVisible="{Binding ShowCheckPreRelease}">
|
||||||
Orientation="Horizontal">
|
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
IsChecked="{Binding IsCheckPreRelease}"
|
IsChecked="{Binding IsCheckPreRelease}"
|
||||||
OffContent=""
|
OffContent=""
|
||||||
OnContent="" />
|
OnContent="" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
|
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}"
|
||||||
</StackPanel>
|
TextWrapping="Wrap" />
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
Style="{StaticResource ToolbarTextBlock}" />
|
Style="{StaticResource ToolbarTextBlock}" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel Grid.IsSharedSizeScope="True">
|
||||||
<materialDesign:Card Width="Auto" Margin="{StaticResource Margin8}">
|
<materialDesign:Card Width="Auto" Margin="{StaticResource Margin8}">
|
||||||
<Grid Margin="{StaticResource Margin8}">
|
<Grid Margin="{StaticResource Margin8}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -40,8 +40,14 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" MinWidth="300" />
|
<ColumnDefinition
|
||||||
<ColumnDefinition Width="Auto" MinWidth="200" />
|
Width="Auto"
|
||||||
|
MinWidth="300"
|
||||||
|
SharedSizeGroup="BackupLabel" />
|
||||||
|
<ColumnDefinition
|
||||||
|
Width="Auto"
|
||||||
|
MinWidth="200"
|
||||||
|
SharedSizeGroup="BackupButton" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
@@ -98,8 +104,14 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" MinWidth="300" />
|
<ColumnDefinition
|
||||||
<ColumnDefinition Width="Auto" MinWidth="200" />
|
Width="Auto"
|
||||||
|
MinWidth="300"
|
||||||
|
SharedSizeGroup="BackupLabel" />
|
||||||
|
<ColumnDefinition
|
||||||
|
Width="Auto"
|
||||||
|
MinWidth="200"
|
||||||
|
SharedSizeGroup="BackupButton" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
|||||||
@@ -85,21 +85,22 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
IsChecked="{Binding IsSelected}" />
|
IsChecked="{Binding IsSelected}" />
|
||||||
|
|
||||||
<StackPanel
|
<DockPanel
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource MarginLeftRight4}"
|
Margin="{StaticResource MarginLeftRight4}"
|
||||||
Orientation="Horizontal"
|
|
||||||
Visibility="{Binding ShowCheckPreRelease, Converter={StaticResource BoolToVisConverter}}">
|
Visibility="{Binding ShowCheckPreRelease, Converter={StaticResource BoolToVisConverter}}">
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
IsChecked="{Binding IsCheckPreRelease}" />
|
IsChecked="{Binding IsCheckPreRelease}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
|
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}"
|
||||||
</StackPanel>
|
TextWrapping="Wrap" />
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
|||||||
Reference in New Issue
Block a user