mirror of
https://github.com/2dust/v2rayN.git
synced 2026-08-10 17:22:06 +03:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e01717d832 | ||
|
|
8b4063b44b | ||
|
|
3136f573dd | ||
|
|
e101b1d7b0 | ||
|
|
31044f449d | ||
|
|
eacf8b0784 | ||
|
|
f347fe1c03 | ||
|
|
71208eb711 | ||
|
|
339eaa6bfd | ||
|
|
717ef7f0a7 | ||
|
|
4a9d83f9dc | ||
|
|
1ac14d0903 | ||
|
|
08d423e1b4 | ||
|
|
9638b4c368 | ||
|
|
1034ce6e02 |
2
.github/workflows/build-windows-x86.yml
vendored
2
.github/workflows/build-windows-x86.yml
vendored
@@ -146,7 +146,7 @@ jobs:
|
|||||||
$xrayTag = "v$xrayVer"
|
$xrayTag = "v$xrayVer"
|
||||||
$singTag = "v$singVer"
|
$singTag = "v$singVer"
|
||||||
|
|
||||||
$xrayUrl = "https://github.com/XTLS/Xray-core/releases/download/$xrayTag/Xray-windows-32.zip"
|
$xrayUrl = "https://github.com/autorepobot/Xray/releases/download/$xrayTag/Xray-windows-32.zip"
|
||||||
$singUrl = "https://github.com/SagerNet/sing-box/releases/download/$singTag/sing-box-$singVer-windows-386.zip"
|
$singUrl = "https://github.com/SagerNet/sing-box/releases/download/$singTag/sing-box-$singVer-windows-386.zip"
|
||||||
|
|
||||||
Write-Host "Bundled Xray version: $xrayVer"
|
Write-Host "Bundled Xray version: $xrayVer"
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ Read the Wiki for usage guides and configuration details.
|
|||||||
| Linux | ✅ | - | ✅ | ✅ | ✅ |
|
| Linux | ✅ | - | ✅ | ✅ | ✅ |
|
||||||
| macOS | ✅ | - | ✅ | - | - |
|
| macOS | ✅ | - | ✅ | - | - |
|
||||||
|
|
||||||
|
Minimum OS requirements: [Release files introduction](https://github.com/2dust/v2rayN/wiki/Release-files-introduction) / 最低系统要求:[发布文件介绍](https://github.com/2dust/v2rayN/wiki/Release-files-introduction)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## GPG Verification / GPG 签名校验
|
## GPG Verification / GPG 签名校验
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
|
|||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>13.7</string>
|
<string>13.6</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.24.5</Version>
|
<Version>7.24.6</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -6,28 +6,28 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
|
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.0" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="12.1.0" />
|
<PackageVersion Include="Avalonia.Desktop" Version="12.1.1" />
|
||||||
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
||||||
<PackageVersion Include="AwesomeAssertions" Version="9.5.0" />
|
<PackageVersion Include="AwesomeAssertions" Version="9.5.0" />
|
||||||
<PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" />
|
<PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" />
|
||||||
<PackageVersion Include="IPNetwork2" Version="4.3.0" />
|
<PackageVersion Include="IPNetwork2" Version="4.3.0" />
|
||||||
<PackageVersion Include="ReactiveUI.Avalonia" Version="12.1.0" />
|
<PackageVersion Include="ReactiveUI.Avalonia" Version="12.1.1" />
|
||||||
<PackageVersion Include="CliWrap" Version="3.10.2" />
|
<PackageVersion Include="CliWrap" Version="3.10.4" />
|
||||||
<PackageVersion Include="Downloader" Version="5.9.5" />
|
<PackageVersion Include="Downloader" Version="5.9.5" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
||||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
||||||
<PackageVersion Include="MaterialDesignThemes" Version="5.3.2" />
|
<PackageVersion Include="MaterialDesignThemes" Version="5.3.2" />
|
||||||
<PackageVersion Include="QRCoder" Version="1.8.0" />
|
<PackageVersion Include="QRCoder" Version="1.8.0" />
|
||||||
<PackageVersion Include="ReactiveUI" Version="24.0.0" />
|
<PackageVersion Include="ReactiveUI" Version="24.1.0" />
|
||||||
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="3.1.0" />
|
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="3.2.0" />
|
||||||
<PackageVersion Include="ReactiveUI.WPF" Version="24.0.0" />
|
<PackageVersion Include="ReactiveUI.WPF" Version="24.1.0" />
|
||||||
<PackageVersion Include="Semi.Avalonia" Version="12.1.0" />
|
<PackageVersion Include="Semi.Avalonia" Version="12.1.0.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="12.0.0" />
|
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="12.0.0" />
|
||||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="12.1.0" />
|
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="12.1.0.1" />
|
||||||
<PackageVersion Include="NLog" Version="6.1.4" />
|
<PackageVersion Include="NLog" Version="6.1.4" />
|
||||||
<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.3.10" />
|
<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="WebDav.Client" Version="2.9.0" />
|
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||||
@@ -36,4 +36,4 @@
|
|||||||
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.22" />
|
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.22" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -251,4 +251,12 @@ internal static class CoreConfigTestFactory
|
|||||||
config.TunModeItem.RouteExcludeAddress = ["10.0.0.1/32", "192.168.1.0/24", "fc00::/7"];
|
config.TunModeItem.RouteExcludeAddress = ["10.0.0.1/32", "192.168.1.0/24", "fc00::/7"];
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config CreateConfigWithTun(ECoreType coreType, bool enableIPv6Address)
|
||||||
|
{
|
||||||
|
var config = CreateConfig(coreType);
|
||||||
|
config.TunModeItem.EnableTun = true;
|
||||||
|
config.TunModeItem.EnableIPv6Address = enableIPv6Address;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,85 @@ public class CoreConfigSingboxServiceTests
|
|||||||
cfg.inbounds.Should().Contain(i => i.type == "tun");
|
cfg.inbounds.Should().Contain(i => i.type == "tun");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateClientConfigContent_TunEnabled_ShouldKeepEmbeddedTunRules()
|
||||||
|
{
|
||||||
|
// The embedded tun rules reject local-network noise (NetBIOS/mDNS, multicast).
|
||||||
|
// They are deserialized into List<Rule4Sbox>, so a schema mismatch in the
|
||||||
|
// embedded template makes JsonUtils.Deserialize return null and silently
|
||||||
|
// drops every one of them.
|
||||||
|
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
|
||||||
|
config.TunModeItem.EnableTun = true;
|
||||||
|
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||||
|
|
||||||
|
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.sing_box);
|
||||||
|
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
|
||||||
|
{
|
||||||
|
IsTunEnabled = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue($"ret msg: {result.Msg}");
|
||||||
|
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
|
||||||
|
|
||||||
|
cfg.route.rules.Should().Contain(
|
||||||
|
r => r.action == "reject"
|
||||||
|
&& r.network != null && r.network.Contains("udp")
|
||||||
|
&& r.port != null && r.port.Contains(5353),
|
||||||
|
"the embedded tun rules must reject mDNS/NetBIOS noise");
|
||||||
|
cfg.route.rules.Should().Contain(
|
||||||
|
r => r.action == "reject"
|
||||||
|
&& r.ip_cidr != null && r.ip_cidr.Contains("224.0.0.0/3"),
|
||||||
|
"the embedded tun rules must reject multicast traffic");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateClientConfigContent_TunEnabled_ShouldRejectTrafficToTunOwnAddresses()
|
||||||
|
{
|
||||||
|
// Regression test: traffic addressed to the TUN interface's own addresses must
|
||||||
|
// never reach an outbound. auto_route hijacks the default route, so `direct`
|
||||||
|
// writes such a packet straight back into the TUN, which routes it to the
|
||||||
|
// outbound again - an infinite loop that pins a CPU core. Observed in the wild
|
||||||
|
// with WebRTC ICE connectivity checks against the TUN's own fc00::/7 ULA
|
||||||
|
// address, sustaining ~8k packets/s out of the TUN interface.
|
||||||
|
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
|
||||||
|
config.TunModeItem.EnableTun = true;
|
||||||
|
config.TunModeItem.EnableIPv6Address = true;
|
||||||
|
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||||
|
|
||||||
|
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.sing_box);
|
||||||
|
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
|
||||||
|
{
|
||||||
|
IsTunEnabled = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue($"ret msg: {result.Msg}");
|
||||||
|
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
|
||||||
|
var tun = cfg.inbounds.First(i => i.type == "tun");
|
||||||
|
tun.address.Should().NotBeNullOrEmpty();
|
||||||
|
|
||||||
|
foreach (var address in tun.address!)
|
||||||
|
{
|
||||||
|
var self = IPAddress.Parse(address.Split('/').First());
|
||||||
|
var hostBits = self.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32;
|
||||||
|
var expected = $"{self}/{hostBits}";
|
||||||
|
cfg.route.rules.Should().Contain(
|
||||||
|
r => r.action == "reject" && r.ip_cidr != null && r.ip_cidr.Contains(expected),
|
||||||
|
$"traffic to the TUN's own address '{address}' must be rejected, not routed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The match has to stay on the addresses themselves. sing-tun derives the TUN's DNS
|
||||||
|
// entry from the address right after the interface's own, and every prefix offered
|
||||||
|
// here leaves room for it, so a prefix match would drop system name lookups too.
|
||||||
|
var dropRule = cfg.route.rules.First(r =>
|
||||||
|
r.action == "reject" && r.method == "drop" && r.ip_cidr?.Count > 0);
|
||||||
|
dropRule.ip_cidr!.Should().OnlyContain(c =>
|
||||||
|
c.EndsWith("/32", StringComparison.Ordinal) || c.EndsWith("/128", StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateClientConfigContent_BindInterface_ShouldUseDialBindInterface()
|
public void GenerateClientConfigContent_BindInterface_ShouldUseDialBindInterface()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -570,6 +570,51 @@ public class CoreConfigV2rayServiceTests
|
|||||||
directOutbound!.streamSettings.sockopt!.domainStrategy.Should().Be("UseIPv4");
|
directOutbound!.streamSettings.sockopt!.domainStrategy.Should().Be("UseIPv4");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(false)]
|
||||||
|
[InlineData(true)]
|
||||||
|
public void GenerateClientConfigContent_Tun_ShouldRouteIPv6IntoTunnel(bool enableIPv6Address)
|
||||||
|
{
|
||||||
|
var config = CoreConfigTestFactory.CreateConfigWithTun(ECoreType.Xray, enableIPv6Address);
|
||||||
|
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||||
|
|
||||||
|
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main");
|
||||||
|
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.Xray);
|
||||||
|
|
||||||
|
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
var cfg = JsonUtils.Deserialize<V2rayConfig>(result.Data!.ToString())!;
|
||||||
|
var tunInbound = cfg.inbounds.FirstOrDefault(i => i.protocol == "tun");
|
||||||
|
|
||||||
|
tunInbound.Should().NotBeNull();
|
||||||
|
tunInbound!.settings.autoSystemRoutingTable.Should().Contain("0.0.0.0/0");
|
||||||
|
tunInbound.settings.autoSystemRoutingTable.Should().Contain("::/0");
|
||||||
|
|
||||||
|
// EnableIPv6Address governs the interface address only, never the routing table.
|
||||||
|
tunInbound.settings.gateway.Should().HaveCount(enableIPv6Address ? 2 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateClientConfigContent_TunRouteExcludeAddress_ShouldIncludeIPv6Ranges()
|
||||||
|
{
|
||||||
|
var config = CoreConfigTestFactory.CreateConfigWithTunRouteExcludeAddress(ECoreType.Xray);
|
||||||
|
config.TunModeItem.EnableIPv6Address = false;
|
||||||
|
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||||
|
|
||||||
|
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main");
|
||||||
|
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.Xray);
|
||||||
|
|
||||||
|
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
var cfg = JsonUtils.Deserialize<V2rayConfig>(result.Data!.ToString())!;
|
||||||
|
var tunInbound = cfg.inbounds.FirstOrDefault(i => i.protocol == "tun");
|
||||||
|
|
||||||
|
tunInbound.Should().NotBeNull();
|
||||||
|
tunInbound!.settings.autoSystemRoutingTable.Should().Contain(x => x.Contains(':'));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateClientConfigContent_TunRouteExcludeAddress()
|
public void GenerateClientConfigContent_TunRouteExcludeAddress()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -753,11 +753,11 @@ public class Utils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetFreePort(int defaultPort = 0)
|
public static int GetFreePort(int defaultPort)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!(defaultPort == 0 || Utils.PortInUse(defaultPort)))
|
if (!PortInUse(defaultPort))
|
||||||
{
|
{
|
||||||
return defaultPort;
|
return defaultPort;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -586,7 +586,6 @@ public static class ConfigHandler
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static async Task<int> AddCustomOutboundServer(Config config, ProfileItem profileItem, bool blDelete, bool toFile = true)
|
public static async Task<int> AddCustomOutboundServer(Config config, ProfileItem profileItem, bool blDelete, bool toFile = true)
|
||||||
{
|
{
|
||||||
var fileName = profileItem.Address;
|
var fileName = profileItem.Address;
|
||||||
@@ -1739,8 +1738,16 @@ public static class ConfigHandler
|
|||||||
SubItem? subItem)
|
SubItem? subItem)
|
||||||
{
|
{
|
||||||
var subRemarks = subItem?.Remarks;
|
var subRemarks = subItem?.Remarks;
|
||||||
// Safe Mode: Only allow full configuration if it's not from a subscription
|
// Prioritize using complete custom parsing, followed by custom outbound parsing.
|
||||||
var lstProfiles = V2rayFmt.ResolveToCustomOutbound(strData, subRemarks);
|
var lstProfiles = V2rayFmt.ResolveToCustom(strData, subRemarks);
|
||||||
|
if (lstProfiles.Count == 0)
|
||||||
|
{
|
||||||
|
lstProfiles = SingboxFmt.ResolveToCustom(strData, subRemarks);
|
||||||
|
}
|
||||||
|
if (lstProfiles.Count == 0)
|
||||||
|
{
|
||||||
|
lstProfiles = V2rayFmt.ResolveToCustomOutbound(strData, subRemarks);
|
||||||
|
}
|
||||||
if (lstProfiles.Count == 0)
|
if (lstProfiles.Count == 0)
|
||||||
{
|
{
|
||||||
lstProfiles = SingboxFmt.ResolveToCustomOutbound(strData, subRemarks);
|
lstProfiles = SingboxFmt.ResolveToCustomOutbound(strData, subRemarks);
|
||||||
@@ -1750,7 +1757,7 @@ public static class ConfigHandler
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = await AddCustomOutboundServers(config, lstProfiles, subid, isSub);
|
var count = await AddBatchCustomServers(config, lstProfiles, subid, isSub);
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
return count;
|
return count;
|
||||||
@@ -1786,7 +1793,7 @@ public static class ConfigHandler
|
|||||||
var subRemarks = subItem.Remarks;
|
var subRemarks = subItem.Remarks;
|
||||||
var customCoreType = subItem.CustomCoreType!.Value;
|
var customCoreType = subItem.CustomCoreType!.Value;
|
||||||
|
|
||||||
List<ProfileItem>? lstProfiles = customCoreType switch
|
var lstProfiles = customCoreType switch
|
||||||
{
|
{
|
||||||
ECoreType.Xray => V2rayFmt.ResolveToCustom(strData, subRemarks),
|
ECoreType.Xray => V2rayFmt.ResolveToCustom(strData, subRemarks),
|
||||||
ECoreType.sing_box => SingboxFmt.ResolveToCustom(strData, subRemarks),
|
ECoreType.sing_box => SingboxFmt.ResolveToCustom(strData, subRemarks),
|
||||||
@@ -1800,7 +1807,7 @@ public static class ConfigHandler
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = await AddCustomOutboundServers(config, lstProfiles, subid, isSub);
|
var count = await AddBatchCustomServers(config, lstProfiles, subid, isSub);
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
return count;
|
return count;
|
||||||
@@ -1810,7 +1817,7 @@ public static class ConfigHandler
|
|||||||
return await SaveCustomRawFileServer(config, strData, subid, isSub, subItem, customCoreType);
|
return await SaveCustomRawFileServer(config, strData, subid, isSub, subItem, customCoreType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> AddCustomOutboundServers(
|
private static async Task<int> AddBatchCustomServers(
|
||||||
Config config,
|
Config config,
|
||||||
List<ProfileItem> lstProfiles,
|
List<ProfileItem> lstProfiles,
|
||||||
string subid,
|
string subid,
|
||||||
@@ -1821,9 +1828,20 @@ public static class ConfigHandler
|
|||||||
{
|
{
|
||||||
it.Subid = subid;
|
it.Subid = subid;
|
||||||
it.IsSub = isSub;
|
it.IsSub = isSub;
|
||||||
if (await AddCustomOutboundServer(config, it, true) == 0)
|
|
||||||
|
if (it.ConfigType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
count++;
|
if (await AddCustomServer(config, it, true) == 0)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (await AddCustomOutboundServer(config, it, true) == 0)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public class SingboxFmt : BaseFmt
|
|||||||
var fileName = WriteAllText(JsonUtils.Serialize(jsonObject));
|
var fileName = WriteAllText(JsonUtils.Serialize(jsonObject));
|
||||||
var profileItem = new ProfileItem
|
var profileItem = new ProfileItem
|
||||||
{
|
{
|
||||||
|
ConfigType = EConfigType.Custom,
|
||||||
CoreType = ECoreType.sing_box,
|
CoreType = ECoreType.sing_box,
|
||||||
Address = fileName,
|
Address = fileName,
|
||||||
Remarks = subRemarks ?? "singbox_custom",
|
Remarks = subRemarks ?? "singbox_custom",
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ public class V2rayFmt : BaseFmt
|
|||||||
|
|
||||||
var profileItem = new ProfileItem
|
var profileItem = new ProfileItem
|
||||||
{
|
{
|
||||||
|
ConfigType = EConfigType.Custom,
|
||||||
CoreType = ECoreType.Xray,
|
CoreType = ECoreType.Xray,
|
||||||
Address = fileName,
|
Address = fileName,
|
||||||
Remarks = jsonObject["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom",
|
Remarks = jsonObject["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom",
|
||||||
|
|||||||
6
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
6
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -178,7 +178,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Failed to import custom configuration Configuration 的本地化字符串。
|
/// 查找类似 Failed to import configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string FailedImportedCustomServer {
|
public static string FailedImportedCustomServer {
|
||||||
get {
|
get {
|
||||||
@@ -268,7 +268,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Please browse to import Configuration configuration 的本地化字符串。
|
/// 查找类似 Please browse to import configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string FillServerAddressCustom {
|
public static string FillServerAddressCustom {
|
||||||
get {
|
get {
|
||||||
@@ -2653,7 +2653,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Custom configuration Configuration imported successfully 的本地化字符串。
|
/// 查找类似 Custom configuration imported successfully 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string SuccessfullyImportedCustomServer {
|
public static string SuccessfullyImportedCustomServer {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
<value>Failed to get the default configuration</value>
|
<value>Failed to get the default configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedImportedCustomServer" xml:space="preserve">
|
<data name="FailedImportedCustomServer" xml:space="preserve">
|
||||||
<value>Failed to import custom configuration Configuration</value>
|
<value>Failed to import configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedReadConfiguration" xml:space="preserve">
|
<data name="FailedReadConfiguration" xml:space="preserve">
|
||||||
<value>Failed to read configuration file</value>
|
<value>Failed to read configuration file</value>
|
||||||
@@ -283,7 +283,7 @@
|
|||||||
<value>Configuration successful. {0}</value>
|
<value>Configuration successful. {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
|
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
|
||||||
<value>Custom configuration Configuration imported successfully</value>
|
<value>Custom configuration imported successfully</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
|
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
|
||||||
<value>{0} Configurations have been imported from clipboard</value>
|
<value>{0} Configurations have been imported from clipboard</value>
|
||||||
@@ -385,7 +385,7 @@
|
|||||||
<value>All</value>
|
<value>All</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||||
<value>Please browse to import Configuration configuration</value>
|
<value>Please browse to import configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Speedtesting" xml:space="preserve">
|
<data name="Speedtesting" xml:space="preserve">
|
||||||
<value>Testing...</value>
|
<value>Testing...</value>
|
||||||
@@ -1860,4 +1860,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||||||
<data name="LvCustomCoreType" xml:space="preserve">
|
<data name="LvCustomCoreType" xml:space="preserve">
|
||||||
<value>Custom config core</value>
|
<value>Custom config core</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -268,7 +268,7 @@
|
|||||||
<value>Сначала выберите сервер</value>
|
<value>Сначала выберите сервер</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemoveDuplicateServerResult" xml:space="preserve">
|
<data name="RemoveDuplicateServerResult" xml:space="preserve">
|
||||||
<value>Дедупликация конфигураций завершена. Было: {0}, Стало: {1}.</value>
|
<value>Дедупликация конфигураций завершена. Было: {0}, Стало: {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemoveServer" xml:space="preserve">
|
<data name="RemoveServer" xml:space="preserve">
|
||||||
<value>Вы уверены, что хотите удалить сервер?</value>
|
<value>Вы уверены, что хотите удалить сервер?</value>
|
||||||
@@ -1048,7 +1048,7 @@
|
|||||||
<value>HTTP-заголовки</value>
|
<value>HTTP-заголовки</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TipHttpOutboundHeaders" xml:space="preserve">
|
<data name="TipHttpOutboundHeaders" xml:space="preserve">
|
||||||
<value>Пользовательские заголовки исходящего HTTP-запроса. Введите JSON-объект, значения которого — строка или массив строк.</value>
|
<value>Пользовательские заголовки исходящего HTTP-запроса. Введите JSON-объект, значения которого — строка или массив строк</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvPrevProfile" xml:space="preserve">
|
<data name="LvPrevProfile" xml:space="preserve">
|
||||||
<value>Псевдоним предыдущего прокси</value>
|
<value>Псевдоним предыдущего прокси</value>
|
||||||
@@ -1120,28 +1120,28 @@
|
|||||||
<value>Длина фрагмента</value>
|
<value>Длина фрагмента</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsFragmentLengthTip" xml:space="preserve">
|
<data name="TbSettingsFragmentLengthTip" xml:space="preserve">
|
||||||
<value>Диапазон размера фрагмента в байтах (например, 50-100). Первое значение не больше второго. Пусто = по умолчанию.</value>
|
<value>Диапазон размера фрагмента в байтах (например, 50-100). Первое значение не больше второго. Пусто = по умолчанию</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
<data name="TbSettingsFragmentInterval" xml:space="preserve">
|
||||||
<value>Интервал фрагментации</value>
|
<value>Интервал фрагментации</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsFragmentIntervalTip" xml:space="preserve">
|
<data name="TbSettingsFragmentIntervalTip" xml:space="preserve">
|
||||||
<value>Задержка между фрагментами в мс (например, 10-20). Диапазон 1-100. Первое значение не больше второго. Пусто = по умолчанию.</value>
|
<value>Задержка между фрагментами в мс (например, 10-20). Диапазон 1-100. Первое значение не больше второго. Пусто = по умолчанию</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsFragmentMaxSplit" xml:space="preserve">
|
<data name="TbSettingsFragmentMaxSplit" xml:space="preserve">
|
||||||
<value>Макс. разделение</value>
|
<value>Макс. разделение</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsFragmentMaxSplitTip" xml:space="preserve">
|
<data name="TbSettingsFragmentMaxSplitTip" xml:space="preserve">
|
||||||
<value>Максимальное число разделений (0 = без лимита, 0-10000). Только для Xray-core. Пусто = по умолчанию.</value>
|
<value>Максимальное число разделений (0 = без лимита, 0-10000). Только для Xray-core. Пусто = по умолчанию</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsFragmentFallbackDelay" xml:space="preserve">
|
<data name="TbSettingsFragmentFallbackDelay" xml:space="preserve">
|
||||||
<value>Резервная задержка</value>
|
<value>Резервная задержка</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsFragmentFallbackDelayTip" xml:space="preserve">
|
<data name="TbSettingsFragmentFallbackDelayTip" xml:space="preserve">
|
||||||
<value>Резервная задержка для TLS-фрагмента. Только для sing-box. Пусто = по умолчанию.</value>
|
<value>Резервная задержка для TLS-фрагмента. Только для sing-box. Пусто = по умолчанию</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillFragmentParameterError" xml:space="preserve">
|
<data name="FillFragmentParameterError" xml:space="preserve">
|
||||||
<value>Неверный формат диапазона. Используйте 'from-to' (например, 50-100).</value>
|
<value>Неверный формат диапазона. Используйте 'from-to' (например, 50-100)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||||
<value>Включить файл кэша для sing-box (файлы наборов правил)</value>
|
<value>Включить файл кэша для sing-box (файлы наборов правил)</value>
|
||||||
@@ -1351,7 +1351,7 @@
|
|||||||
<value>Пароль sudo системы</value>
|
<value>Пароль sudo системы</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||||
<value>Пароль sudo будет проверен в терминале. Если из-за ошибки проверки приложение начнёт работать некорректно, перезапустите его. Пароль не сохраняется — его нужно вводить после каждого перезапуска.</value>
|
<value>Пароль sudo будет проверен в терминале. Если из-за ошибки проверки приложение начнёт работать некорректно, перезапустите его. Пароль не сохраняется — его нужно вводить после каждого перезапуска</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TransportHeaderType5" xml:space="preserve">
|
<data name="TransportHeaderType5" xml:space="preserve">
|
||||||
<value>XHTTP-режим</value>
|
<value>XHTTP-режим</value>
|
||||||
@@ -1411,7 +1411,7 @@
|
|||||||
<value>Можно указать псевдоним из конфигурации, убедитесь, что он существует и уникален</value>
|
<value>Можно указать псевдоним из конфигурации, убедитесь, что он существует и уникален</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||||
<value>Неверный пароль, попробуйте ещё раз.</value>
|
<value>Неверный пароль, попробуйте ещё раз</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbMldsa65Verify" xml:space="preserve">
|
<data name="TbMldsa65Verify" xml:space="preserve">
|
||||||
<value>Mldsa65Verify</value>
|
<value>Mldsa65Verify</value>
|
||||||
@@ -1492,7 +1492,7 @@
|
|||||||
<value>Добавляет только конфигурацию Outbound и Endpoint. Нажмите, чтобы открыть документ</value>
|
<value>Добавляет только конфигурацию Outbound и Endpoint. Нажмите, чтобы открыть документ</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||||
<value>Эта функция предназначена для продвинутых пользователей и особых случаев. После включения игнорируются базовые настройки ядра, DNS и маршрутизации. Вы должны самостоятельно корректно задать порт системного прокси, учёт трафика и другие связанные параметры — всё настраивается вручную.</value>
|
<value>Эта функция предназначена для продвинутых пользователей и особых случаев. После включения игнорируются базовые настройки ядра, DNS и маршрутизации. Вы должны самостоятельно корректно задать порт системного прокси, учёт трафика и другие связанные параметры — всё настраивается вручную</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgStartParsingSubscription" xml:space="preserve">
|
<data name="MsgStartParsingSubscription" xml:space="preserve">
|
||||||
<value>Начинается разбор и обработка содержимого подписки</value>
|
<value>Начинается разбор и обработка содержимого подписки</value>
|
||||||
@@ -1501,7 +1501,7 @@
|
|||||||
<value>Выбрать профиль</value>
|
<value>Выбрать профиль</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>Applies globally by default, and built-in FakeIP filtering is only built into sing-box.</value>
|
<value>Применяется глобально по умолчанию; встроенная фильтрация FakeIP есть только в sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
<value>Добавьте хотя бы одну конфигурацию</value>
|
<value>Добавьте хотя бы одну конфигурацию</value>
|
||||||
@@ -1588,7 +1588,7 @@
|
|||||||
<value>Привязанный сертификат (заполните любое из полей)
|
<value>Привязанный сертификат (заполните любое из полей)
|
||||||
При указании сертификат будет привязан, а «Пропустить проверку сертификата» отключится.
|
При указании сертификат будет привязан, а «Пропустить проверку сертификата» отключится.
|
||||||
|
|
||||||
Получение сертификата может завершиться неудачей при использовании самоподписанного сертификата или при наличии ненадёжного / вредоносного ЦС в системе.</value>
|
Получение сертификата может завершиться неудачей при использовании самоподписанного сертификата или при наличии ненадёжного / вредоносного ЦС в системе</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFetchCert" xml:space="preserve">
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
<value>Получить сертификат</value>
|
<value>Получить сертификат</value>
|
||||||
@@ -1642,10 +1642,10 @@
|
|||||||
<value>По умолчанию используется только при разрешении имён в процессе маршрутизации; убедитесь, что удалённый сервер может достичь этого DNS</value>
|
<value>По умолчанию используется только при разрешении имён в процессе маршрутизации; убедитесь, что удалённый сервер может достичь этого DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
<value>Если не задано или «AsIs», используется системный DNS; иначе — встроенный DNS-модуль.</value>
|
<value>Если не задано или «AsIs», используется системный DNS; иначе — встроенный DNS-модуль</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>Если не задано или «AsIs», разрешение DNS выполняется DNS удалённого сервера; иначе — встроенный DNS-модуль.</value>
|
<value>Если не задано или «AsIs», разрешение DNS выполняется DNS удалённого сервера; иначе — встроенный DNS-модуль</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>Интервал смены портов (Port Hopping)</value>
|
<value>Интервал смены портов (Port Hopping)</value>
|
||||||
@@ -1660,37 +1660,37 @@
|
|||||||
<value>Правило маршрутизации {0}, исходящий узел {1}, предупреждение: {2}</value>
|
<value>Правило маршрутизации {0}, исходящий узел {1}, предупреждение: {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgRoutingRuleOutboundNodeError" xml:space="preserve">
|
<data name="MsgRoutingRuleOutboundNodeError" xml:space="preserve">
|
||||||
<value>Правило маршрутизации {0}, исходящий узел {1}, ошибка: {2}. Используется только прокси-узел.</value>
|
<value>Правило маршрутизации {0}, исходящий узел {1}, ошибка: {2}. Используется только прокси-узел</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgGroupCycleDependency" xml:space="preserve">
|
<data name="MsgGroupCycleDependency" xml:space="preserve">
|
||||||
<value>Группа {0} имеет циклическую зависимость на дочерний узел {1}. Узел пропущен.</value>
|
<value>Группа {0} имеет циклическую зависимость на дочерний узел {1}. Узел пропущен</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgGroupChildNodeWarning" xml:space="preserve">
|
<data name="MsgGroupChildNodeWarning" xml:space="preserve">
|
||||||
<value>Группа {0}: предупреждение дочернего узла {1}: {2}</value>
|
<value>Группа {0}: предупреждение дочернего узла {1}: {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgGroupChildNodeError" xml:space="preserve">
|
<data name="MsgGroupChildNodeError" xml:space="preserve">
|
||||||
<value>Группа {0}: ошибка дочернего узла {1}: {2}. Узел пропущен.</value>
|
<value>Группа {0}: ошибка дочернего узла {1}: {2}. Узел пропущен</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgGroupChildGroupNodeWarning" xml:space="preserve">
|
<data name="MsgGroupChildGroupNodeWarning" xml:space="preserve">
|
||||||
<value>Группа {0}: предупреждение дочернего узла группы {1}: {2}</value>
|
<value>Группа {0}: предупреждение дочернего узла группы {1}: {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgGroupChildGroupNodeError" xml:space="preserve">
|
<data name="MsgGroupChildGroupNodeError" xml:space="preserve">
|
||||||
<value>Группа {0}: ошибка дочернего узла группы {1}: {2}. Узел пропущен.</value>
|
<value>Группа {0}: ошибка дочернего узла группы {1}: {2}. Узел пропущен</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
|
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
|
||||||
<value>У группы {0} нет допустимых дочерних узлов.</value>
|
<value>У группы {0} нет допустимых дочерних узлов</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
|
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
|
||||||
<value>У правила маршрутизации {0} пустой исходящий тег. Используется только прокси-узел.</value>
|
<value>У правила маршрутизации {0} пустой исходящий тег. Используется только прокси-узел</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
|
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
|
||||||
<value>Правило маршрутизации {0}, исходящий узел {1} не найден. Используется только прокси-узел.</value>
|
<value>Правило маршрутизации {0}, исходящий узел {1} не найден. Используется только прокси-узел</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
|
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
|
||||||
<value>Предыдущий прокси подписки {0} не найден. Пропущено.</value>
|
<value>Предыдущий прокси подписки {0} не найден. Пропущено</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||||
<value>Следующий прокси подписки {0} не найден. Пропущено.</value>
|
<value>Следующий прокси подписки {0} не найден. Пропущено</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupServer" xml:space="preserve">
|
<data name="menuGenGroupServer" xml:space="preserve">
|
||||||
<value>Сгенерировать группу политик</value>
|
<value>Сгенерировать группу политик</value>
|
||||||
@@ -1756,7 +1756,7 @@
|
|||||||
<value>Привязать интерфейс</value>
|
<value>Привязать интерфейс</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
|
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
|
||||||
<value>Для среды с несколькими сетевыми интерфейсами укажите имя интерфейса для исходящих подключений. На Linux/macOS работает только при включённом TUN.</value>
|
<value>Для среды с несколькими сетевыми интерфейсами укажите имя интерфейса для исходящих подключений. На Linux/macOS работает только при включённом TUN</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbPreSharedKey" xml:space="preserve">
|
<data name="TbPreSharedKey" xml:space="preserve">
|
||||||
<value>Общий ключ (PSK)</value>
|
<value>Общий ключ (PSK)</value>
|
||||||
@@ -1780,13 +1780,13 @@
|
|||||||
<value>Доступно обновление</value>
|
<value>Доступно обновление</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
||||||
<value>Предупреждение: 1 августа 2026 г. Xray отключит пропуск проверки сертификата (allowInsecure). Как можно скорее перейдите на привязанный отпечаток сертификата (pinnedPeerCertSha256). После этой даты allowInsecure использовать будет нельзя.</value>
|
<value>Предупреждение: 1 августа 2026 г. Xray отключит пропуск проверки сертификата (allowInsecure). Как можно скорее перейдите на привязанный отпечаток сертификата (pinnedPeerCertSha256). После этой даты allowInsecure использовать будет нельзя</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRouteExcludeAddress" xml:space="preserve">
|
<data name="TbRouteExcludeAddress" xml:space="preserve">
|
||||||
<value>Адреса, исключаемые из маршрутизации</value>
|
<value>Адреса, исключаемые из маршрутизации</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRouteExcludeAddressTip" xml:space="preserve">
|
<data name="TbRouteExcludeAddressTip" xml:space="preserve">
|
||||||
<value>Разделяйте запятыми (,).</value>
|
<value>Разделяйте запятыми (,)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgTunRouteExcludeInvalidAddress" xml:space="preserve">
|
<data name="MsgTunRouteExcludeInvalidAddress" xml:space="preserve">
|
||||||
<value>Недопустимый адрес в списке исключений маршрутизации TUN: {0}</value>
|
<value>Недопустимый адрес в списке исключений маршрутизации TUN: {0}</value>
|
||||||
@@ -1798,22 +1798,22 @@
|
|||||||
<value>Включить финальную фрагментацию (Final Fragment)</value>
|
<value>Включить финальную фрагментацию (Final Fragment)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
|
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
|
||||||
<value>Разбивать конец пакетов на более мелкие фрагменты при отправке. Это может влиять на пропускную способность и задержку.</value>
|
<value>Разбивать конец пакетов на более мелкие фрагменты при отправке. Это может влиять на пропускную способность и задержку</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbEnabletDnsViaProxy" xml:space="preserve">
|
<data name="TbEnabletDnsViaProxy" xml:space="preserve">
|
||||||
<value>DNS через Bridge</value>
|
<value>DNS через Bridge</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgInsecureConfiguration" xml:space="preserve">
|
<data name="MsgInsecureConfiguration" xml:space="preserve">
|
||||||
<value>Обнаружена небезопасная конфигурация: AllowInsecure включён, но сертификат не предоставлен. Это может привести к атаке «человек посередине» (MITM).</value>
|
<value>Обнаружена небезопасная конфигурация: AllowInsecure включён, но сертификат не предоставлен. Это может привести к атаке «человек посередине» (MITM)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbHy2RealmUrl" xml:space="preserve">
|
<data name="TbHy2RealmUrl" xml:space="preserve">
|
||||||
<value>Realm URL</value>
|
<value>Realm URL</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidHy2RealmUrl" xml:space="preserve">
|
<data name="InvalidHy2RealmUrl" xml:space="preserve">
|
||||||
<value>Некорректный Realm URL.</value>
|
<value>Некорректный Realm URL</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidHttpOutboundHeaders" xml:space="preserve">
|
<data name="InvalidHttpOutboundHeaders" xml:space="preserve">
|
||||||
<value>Введите корректный JSON заголовков HTTP-запроса.</value>
|
<value>Введите корректный JSON заголовков HTTP-запроса</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbHy2RealmUrlTip" xml:space="preserve">
|
<data name="TbHy2RealmUrlTip" xml:space="preserve">
|
||||||
<value>Формат: realm://<token>@<rendezvous-host>[:port]/<realm-name>?stun=<stun-host>[:port]</value>
|
<value>Формат: realm://<token>@<rendezvous-host>[:port]/<realm-name>?stun=<stun-host>[:port]</value>
|
||||||
@@ -1822,12 +1822,42 @@
|
|||||||
<value>Размер пакета Gecko (мин/макс)</value>
|
<value>Размер пакета Gecko (мин/макс)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbLegacyProtectTip" xml:space="preserve">
|
<data name="TbLegacyProtectTip" xml:space="preserve">
|
||||||
<value>Если включено, используется sing-box TUN; иначе — xray TUN.</value>
|
<value>Если включено, используется sing-box TUN; иначе — xray TUN</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRootCertificateProvider" xml:space="preserve">
|
<data name="TbRootCertificateProvider" xml:space="preserve">
|
||||||
<value>Поставщик корневых сертификатов</value>
|
<value>Поставщик корневых сертификатов</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRootCertificateProviderTip" xml:space="preserve">
|
<data name="TbRootCertificateProviderTip" xml:space="preserve">
|
||||||
<value>Применяется только к загрузкам и сетевым запросам графического интерфейса v2rayN. Не влияет на проверку сертификатов ядром.</value>
|
<value>Применяется только к загрузкам и сетевым запросам графического интерфейса v2rayN. Не влияет на проверку сертификатов ядром</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbProxyDialResolveStrategy" xml:space="preserve">
|
||||||
|
<value>Стратегия разрешения DNS при подключении к прокси</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbProxyDialResolveStrategyTip" xml:space="preserve">
|
||||||
|
<value>Не рекомендуется; может вызвать петли маршрутизации</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEnableHappyEyeballs" xml:space="preserve">
|
||||||
|
<value>Включить Happy Eyeballs</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEnableHappyEyeballsTip" xml:space="preserve">
|
||||||
|
<value>Требуется стратегия UseIP. При включении одновременно устанавливаются соединения по IPv4 и IPv6 и автоматически выбирается более быстрый доступный путь</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbIpv6Address" xml:space="preserve">
|
||||||
|
<value>Адрес IPv6</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbIpv4Address" xml:space="preserve">
|
||||||
|
<value>Адрес IPv4</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddCustomOutboundServer" xml:space="preserve">
|
||||||
|
<value>Добавить пользовательский outbound</value>
|
||||||
|
</data>
|
||||||
|
<data name="MsgCustomOutboundFileNotFound" xml:space="preserve">
|
||||||
|
<value>Файл пользовательского outbound {0} не найден: {1}</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCustomOutboundTip" xml:space="preserve">
|
||||||
|
<value>Для xray/sing-box поддерживается только один outbound/endpoint</value>
|
||||||
|
</data>
|
||||||
|
<data name="LvCustomCoreType" xml:space="preserve">
|
||||||
|
<value>Ядро пользовательской конфигурации</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1849,4 +1849,16 @@
|
|||||||
<data name="TbIpv4Address" xml:space="preserve">
|
<data name="TbIpv4Address" xml:space="preserve">
|
||||||
<value>Ipv4 位址</value>
|
<value>Ipv4 位址</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
<data name="menuAddCustomOutboundServer" xml:space="preserve">
|
||||||
|
<value>新增自訂出站</value>
|
||||||
|
</data>
|
||||||
|
<data name="MsgCustomOutboundFileNotFound" xml:space="preserve">
|
||||||
|
<value>自訂出站 {0} 的檔案未找到:{1}</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCustomOutboundTip" xml:space="preserve">
|
||||||
|
<value>僅支援 xray/sing-box 的單一 outbound/endpoint</value>
|
||||||
|
</data>
|
||||||
|
<data name="LvCustomCoreType" xml:space="preserve">
|
||||||
|
<value>自訂設定核心</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
|
|||||||
@@ -21,26 +21,27 @@ public partial class CoreConfigSingboxService
|
|||||||
};
|
};
|
||||||
_coreConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
|
var inboundConf = _config.Inbound.First();
|
||||||
inbound.listen_port = listenPort;
|
inbound.listen_port = listenPort;
|
||||||
|
|
||||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
if (inboundConf.SecondLocalPortEnabled)
|
||||||
{
|
{
|
||||||
var inbound2 = BuildInbound(inbound, EInboundProtocol.socks2, true);
|
var inbound2 = BuildInbound(inbound, EInboundProtocol.socks2, true);
|
||||||
_coreConfig.inbounds.Add(inbound2);
|
_coreConfig.inbounds.Add(inbound2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Inbound.First().AllowLANConn)
|
if (inboundConf.AllowLANConn)
|
||||||
{
|
{
|
||||||
if (_config.Inbound.First().NewPort4LAN)
|
if (inboundConf.NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = BuildInbound(inbound, EInboundProtocol.socks3, true);
|
var inbound3 = BuildInbound(inbound, EInboundProtocol.socks3, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
_coreConfig.inbounds.Add(inbound3);
|
_coreConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
if (inboundConf.User.IsNotEmpty() && inboundConf.Pass.IsNotEmpty())
|
||||||
{
|
{
|
||||||
inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } };
|
inbound3.users = new() { new() { username = inboundConf.User, password = inboundConf.Pass } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -48,12 +48,18 @@ public partial class CoreConfigSingboxService
|
|||||||
// packet straight back into the TUN, which hands it to the outbound again -
|
// packet straight back into the TUN, which hands it to the outbound again -
|
||||||
// an infinite loop that pins a CPU core. Drop instead of rejecting so no
|
// an infinite loop that pins a CPU core. Drop instead of rejecting so no
|
||||||
// ICMP unreachable is generated back towards the same addresses.
|
// ICMP unreachable is generated back towards the same addresses.
|
||||||
|
//
|
||||||
|
// Match each address on its own, not the prefix it carries. On Linux sing-tun
|
||||||
|
// registers Inet4Address[0].Addr().Next() with systemd-resolved as a "~." DNS
|
||||||
|
// upstream, and every prefix offered here is a /30 or /126, so carrying the
|
||||||
|
// prefix through would cover that resolver address too and drop every system
|
||||||
|
// name lookup along with the loop.
|
||||||
var tunAddresses = _coreConfig.inbounds.FirstOrDefault(i => i.type == "tun")?.address;
|
var tunAddresses = _coreConfig.inbounds.FirstOrDefault(i => i.type == "tun")?.address;
|
||||||
if (tunAddresses?.Count > 0)
|
if (tunAddresses?.Count > 0)
|
||||||
{
|
{
|
||||||
_coreConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
ip_cidr = [.. tunAddresses],
|
ip_cidr = [.. tunAddresses.Select(ToSingleAddressPrefix)],
|
||||||
action = "reject",
|
action = "reject",
|
||||||
method = "drop",
|
method = "drop",
|
||||||
});
|
});
|
||||||
@@ -284,6 +290,14 @@ public partial class CoreConfigSingboxService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string ToSingleAddressPrefix(string address)
|
||||||
|
{
|
||||||
|
var addr = address.Split('/').First();
|
||||||
|
return IPAddress.TryParse(addr, out var ip)
|
||||||
|
? $"{addr}/{(ip.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32)}"
|
||||||
|
: address;
|
||||||
|
}
|
||||||
|
|
||||||
private List<string> BuildRoutingDirectExe()
|
private List<string> BuildRoutingDirectExe()
|
||||||
{
|
{
|
||||||
var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|||||||
@@ -9,37 +9,38 @@ public partial class CoreConfigV2rayService
|
|||||||
var listen = "0.0.0.0";
|
var listen = "0.0.0.0";
|
||||||
var listenPort = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
var listenPort = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||||
_coreConfig.inbounds = [];
|
_coreConfig.inbounds = [];
|
||||||
var inbound = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
var inboundConf = _config.Inbound.First();
|
||||||
|
var inbound = BuildInbound(inboundConf, EInboundProtocol.socks, true);
|
||||||
var isUsingLocalMixedPort = _node.Address == Global.Loopback && _node.Port == listenPort;
|
var isUsingLocalMixedPort = _node.Address == Global.Loopback && _node.Port == listenPort;
|
||||||
|
|
||||||
if (!context.IsTunEnabled || !isUsingLocalMixedPort)
|
if (!context.IsTunEnabled || !isUsingLocalMixedPort)
|
||||||
{
|
{
|
||||||
_coreConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
if (inboundConf.SecondLocalPortEnabled)
|
||||||
{
|
{
|
||||||
var inbound2 = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
var inbound2 = BuildInbound(inboundConf, EInboundProtocol.socks2, true);
|
||||||
_coreConfig.inbounds.Add(inbound2);
|
_coreConfig.inbounds.Add(inbound2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Inbound.First().AllowLANConn)
|
if (inboundConf.AllowLANConn)
|
||||||
{
|
{
|
||||||
if (_config.Inbound.First().NewPort4LAN)
|
if (inboundConf.NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
|
var inbound3 = BuildInbound(inboundConf, EInboundProtocol.socks3, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
_coreConfig.inbounds.Add(inbound3);
|
_coreConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
if (inboundConf.User.IsNotEmpty() && inboundConf.Pass.IsNotEmpty())
|
||||||
{
|
{
|
||||||
inbound3.settings.auth = "password";
|
inbound3.settings.auth = "password";
|
||||||
inbound3.settings.accounts =
|
inbound3.settings.accounts =
|
||||||
[
|
[
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
user = _config.Inbound.First().User,
|
user = inboundConf.User,
|
||||||
pass = _config.Inbound.First().Pass,
|
pass = inboundConf.Pass,
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
@@ -66,20 +67,23 @@ 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];
|
||||||
tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0"];
|
// Route both families into the tunnel regardless of EnableIPv6Address. That option only
|
||||||
|
// controls whether the interface gets an IPv6 address; leaving ::/0 out of the routing
|
||||||
|
// table makes IPv6 follow the system default route and bypass the tunnel entirely.
|
||||||
|
tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0", "::/0"];
|
||||||
if (_config.TunModeItem.EnableIPv6Address == true)
|
if (_config.TunModeItem.EnableIPv6Address == true)
|
||||||
{
|
{
|
||||||
var address6 = _config.TunModeItem.IPv6Address.NullIfEmpty() ?? Global.TunIPv6Address.First();
|
var address6 = _config.TunModeItem.IPv6Address.NullIfEmpty() ?? Global.TunIPv6Address.First();
|
||||||
tunInbound.settings.gateway.Add(address6);
|
tunInbound.settings.gateway.Add(address6);
|
||||||
tunInbound.settings.autoSystemRoutingTable.Add("::/0");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var bindInterface = _config.CoreBasicItem.BindInterface?.TrimEx();
|
var bindInterface = _config.CoreBasicItem.BindInterface?.TrimEx();
|
||||||
if (!bindInterface.IsNullOrEmpty())
|
if (!bindInterface.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
tunInbound.settings.autoOutboundsInterface = bindInterface;
|
tunInbound.settings.autoOutboundsInterface = bindInterface;
|
||||||
}
|
}
|
||||||
tunInbound.sniffing = inbound.sniffing;
|
tunInbound.sniffing = inbound.sniffing;
|
||||||
|
// tunInbound.sniffing.routeOnly = inbound.sniffing.routeOnly;
|
||||||
tunInbound.sniffing.routeOnly = true;
|
tunInbound.sniffing.routeOnly = true;
|
||||||
|
|
||||||
if (_config.TunModeItem.RouteExcludeAddress is { Count: > 0 })
|
if (_config.TunModeItem.RouteExcludeAddress is { Count: > 0 })
|
||||||
@@ -117,15 +121,8 @@ public partial class CoreConfigV2rayService
|
|||||||
includeList = IPNetwork2.Supernet(includeList.ToArray()).ToList();
|
includeList = IPNetwork2.Supernet(includeList.ToArray()).ToList();
|
||||||
includeListV6 = IPNetwork2.Supernet(includeListV6.ToArray()).ToList();
|
includeListV6 = IPNetwork2.Supernet(includeListV6.ToArray()).ToList();
|
||||||
|
|
||||||
if (_config.TunModeItem.EnableIPv6Address)
|
tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString())
|
||||||
{
|
.Concat(includeListV6.Select(x => x.ToString())).ToList();
|
||||||
tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString())
|
|
||||||
.Concat(includeListV6.Select(x => x.ToString())).ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString()).ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_coreConfig.inbounds.Add(tunInbound);
|
_coreConfig.inbounds.Add(tunInbound);
|
||||||
|
|||||||
Reference in New Issue
Block a user