Compare commits

..

10 Commits

Author SHA1 Message Date
2dust 92c3df45ae up 7.24.3 2026-07-26 21:25:31 +08:00
hyjhyj111 ee7e21268a Fix TUN core elevation: decide sudo from the config context snapshot (#9830)
CoreManager.RunProcess decided sudo elevation from the live mutable
_config.TunModeItem.EnableTun while the launched config was generated
from the immutable CoreConfigContext snapshot. If the TUN state changed
while a reload was in flight, a core whose config contains a TUN
inbound could launch without elevation and die within the 100ms health
check ("Failed to run core"), with no stderr shown.

The elevation decision now follows context.IsTunEnabled /
preContext.IsTunEnabled, so the generated config and the launch mode
always agree. With legacy TUN protect, the sing-box pre-core hosting
TUN is elevated and the main core no longer runs as root needlessly.
2026-07-26 21:24:11 +08:00
2dust dc216c2b02 Rename TunIp to TunIP 2026-07-26 21:07:36 +08:00
2dust beffa919d3 Use /126 prefixes for TUN IPv6 addresses
https://github.com/2dust/v2rayN/pull/9830
2026-07-26 20:46:32 +08:00
2dust a654826231 Enable legacy TUN protect by default 2026-07-26 20:46:01 +08:00
2dust b01476d147 Fix tun inbound dns setting 2026-07-26 10:29:35 +08:00
2dust cf8dd45abe up 7.24.2 2026-07-24 16:57:53 +08:00
2dust 9bba6f53f8 Update Directory.Packages.props 2026-07-24 16:57:22 +08:00
2dust cd77f1d882 Code clean 2026-07-24 16:57:19 +08:00
mymyme 0427037638 fix: sanitize system proxy exceptions to avoid macOS ExceptionsList b… (#9815)
* fix: sanitize system proxy exceptions to avoid macOS ExceptionsList being dropped

On macOS/Linux the exception list is comma-separated and passed to networksetup. It was only stripped of spaces, so a stray newline or a trailing comma left a malformed/empty entry. macOS configd then silently rejects the whole ExceptionsList (scutil --proxy shows no ExceptionsList), so the bypass never takes effect. Split on ',' with TrimEntries + RemoveEmptyEntries and rejoin; behavior-preserving for well-formed input. Windows path (';') is left unchanged.

Note: not built locally (no .NET SDK on hand); relying on CI.

* Update SysProxyHandler.cs

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2026-07-24 14:12:10 +08:00
20 changed files with 194 additions and 80 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.24.1</Version> <Version>7.24.3</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
+2 -2
View File
@@ -9,12 +9,12 @@
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.0" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.0" />
<PackageVersion Include="Avalonia.Desktop" Version="12.1.0" /> <PackageVersion Include="Avalonia.Desktop" Version="12.1.0" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" /> <PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
<PackageVersion Include="AwesomeAssertions" Version="9.4.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.0.3" /> <PackageVersion Include="ReactiveUI.Avalonia" Version="12.0.3" />
<PackageVersion Include="CliWrap" Version="3.10.2" /> <PackageVersion Include="CliWrap" Version="3.10.2" />
<PackageVersion Include="Downloader" Version="5.9.4" /> <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" />
@@ -592,4 +592,38 @@ public class CoreConfigSingboxServiceTests
proxy.realm.stun_servers.Should().Contain("turn.cloudflare.com:3478"); proxy.realm.stun_servers.Should().Contain("turn.cloudflare.com:3478");
proxy.server.Should().BeNull(); proxy.server.Should().BeNull();
} }
[Fact]
public void GenerateClientConfigContent_TunSystemStackWithIpv6_ShouldUsePrefixWithPeerAddress()
{
// Regression test for #9820: sing-box fails with "need one more IPv6 address in
// first prefix for system stack" when the TUN inbound uses a /128 IPv6 prefix.
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
config.TunModeItem.EnableTun = true;
config.TunModeItem.Stack = "system";
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 prefixLength = int.Parse(address[(address.LastIndexOf('/') + 1)..]);
var isIpv6 = address.Contains(':');
prefixLength.Should().BeLessThanOrEqualTo(isIpv6 ? 126 : 30,
$"'{address}' must leave room for the peer address the system stack derives");
}
}
} }
@@ -0,0 +1,44 @@
using AwesomeAssertions;
using ServiceLib.Enums;
using ServiceLib.Manager;
using Xunit;
namespace ServiceLib.Tests.Manager;
public class CoreManagerTests
{
[Theory]
[InlineData(ECoreType.sing_box)]
[InlineData(ECoreType.mihomo)]
[InlineData(ECoreType.Xray)]
public void ShouldRunAsSudo_TunLaunchOnNonWindows_RequiresElevation(ECoreType coreType)
{
CoreManager.ShouldRunAsSudo(isTunLaunch: true, coreType, isNonWindows: true).Should().BeTrue();
}
[Fact]
public void ShouldRunAsSudo_NonTunLaunch_ShouldNotElevate()
{
// Regression guard for the macOS TUN failure: the elevation decision must follow
// the context snapshot that generated the config. A launch whose snapshot has TUN
// disabled must never elevate, and a launch whose snapshot has TUN enabled must
// elevate regardless of later changes to the live config.
CoreManager.ShouldRunAsSudo(isTunLaunch: false, ECoreType.sing_box, isNonWindows: true).Should().BeFalse();
CoreManager.ShouldRunAsSudo(isTunLaunch: false, ECoreType.Xray, isNonWindows: true).Should().BeFalse();
}
[Fact]
public void ShouldRunAsSudo_OnWindows_ShouldNotElevate()
{
CoreManager.ShouldRunAsSudo(isTunLaunch: true, ECoreType.sing_box, isNonWindows: false).Should().BeFalse();
}
[Theory]
[InlineData(ECoreType.v2fly)]
[InlineData(ECoreType.hysteria)]
[InlineData(null)]
public void ShouldRunAsSudo_UnsupportedCoreType_ShouldNotElevate(ECoreType? coreType)
{
CoreManager.ShouldRunAsSudo(isTunLaunch: true, coreType, isNonWindows: true).Should().BeFalse();
}
}
+12 -10
View File
@@ -745,7 +745,7 @@ public class Global
MozillaRootProvider, MozillaRootProvider,
]; ];
public static readonly IReadOnlyList<string> TunIpv4Address = public static readonly IReadOnlyList<string> TunIPv4Address =
[ [
"172.18.0.1/30", "172.18.0.1/30",
"172.31.0.1/30", "172.31.0.1/30",
@@ -757,15 +757,17 @@ public class Global
"10.0.0.1/30", "10.0.0.1/30",
]; ];
public static readonly IReadOnlyList<string> TunIpv6Address = // Prefixes must leave room for a peer address (max /126); the sing-box system
// stack derives a gateway from the first prefix and rejects single-address prefixes.
public static readonly IReadOnlyList<string> TunIPv6Address =
[ [
"fc00::172:18:0:1/128", "fc00::172:18:0:1/126",
"fc00::172:31:0:1/128", "fc00::172:31:0:1/126",
"fc00::172:20:0:1/128", "fc00::172:20:0:1/126",
"fc00::172:16:0:1/128", "fc00::172:16:0:1/126",
"fc00::192:168:100:1/128", "fc00::192:168:100:1/126",
"fc00::10:10:14:1/128", "fc00::10:10:14:1/126",
"fc00::10:1:0:1/128", "fc00::10:1:0:1/126",
"fc00::10:0:0:1/128", "fc00::10:0:0:1/126",
]; ];
} }
+1 -1
View File
@@ -30,8 +30,8 @@ global using ServiceLib.Handler.Fmt;
global using ServiceLib.Handler.SysProxy; global using ServiceLib.Handler.SysProxy;
global using ServiceLib.Helper; global using ServiceLib.Helper;
global using ServiceLib.Manager; global using ServiceLib.Manager;
global using ServiceLib.Models.CoreConfigs;
global using ServiceLib.Models.Configs; global using ServiceLib.Models.Configs;
global using ServiceLib.Models.CoreConfigs;
global using ServiceLib.Models.Dto; global using ServiceLib.Models.Dto;
global using ServiceLib.Models.Entities; global using ServiceLib.Models.Entities;
global using ServiceLib.Resx; global using ServiceLib.Resx;
+1 -1
View File
@@ -92,7 +92,7 @@ public static class ConfigHandler
EnableTun = false, EnableTun = false,
Mtu = 9000, Mtu = 9000,
IcmpRouting = Global.TunIcmpRoutingPolicies.First(), IcmpRouting = Global.TunIcmpRoutingPolicies.First(),
EnableLegacyProtect = false, EnableLegacyProtect = true,
}; };
config.GuiItem ??= new(); config.GuiItem ??= new();
if (!Global.RootCertProviders.Contains(config.GuiItem.RootCertProvider)) if (!Global.RootCertProviders.Contains(config.GuiItem.RootCertProvider))
@@ -16,7 +16,6 @@ public static class SysProxyHandler
try try
{ {
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (port <= 0) if (port <= 0)
{ {
return false; return false;
@@ -24,17 +23,18 @@ public static class SysProxyHandler
switch (type) switch (type)
{ {
case ESysProxyType.ForcedChange when Utils.IsWindows(): case ESysProxyType.ForcedChange when Utils.IsWindows():
{ var (strProxy, strExceptions) = GetWindowsProxyString(config, port);
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions); ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2); break;
break;
}
case ESysProxyType.ForcedChange when Utils.IsLinux(): case ESysProxyType.ForcedChange when Utils.IsLinux():
var exceptions = SanitizeExceptions(config);
await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions); await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
break; break;
case ESysProxyType.ForcedChange when Utils.IsMacOS(): case ESysProxyType.ForcedChange when Utils.IsMacOS():
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions); var exceptions2 = SanitizeExceptions(config);
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions2);
break; break;
case ESysProxyType.ForcedClear when Utils.IsWindows(): case ESysProxyType.ForcedClear when Utils.IsWindows():
@@ -66,15 +66,31 @@ public static class SysProxyHandler
return true; return true;
} }
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions) private static string SanitizeExceptions(Config config)
{ {
strExceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", ""); var exceptions = config.SystemProxyItem.SystemProxyExceptions;
if (exceptions.IsNullOrEmpty())
{
return string.Empty;
}
var items = exceptions
.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
.Select(item => item.Replace(" ", string.Empty))
.Where(item => item.Length > 0);
return string.Join(',', items);
}
private static (string strProxy, string strExceptions) GetWindowsProxyString(Config config, int port)
{
var strExceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (config.SystemProxyItem.NotProxyLocalAddress) if (config.SystemProxyItem.NotProxyLocalAddress)
{ {
strExceptions = $"<local>;{strExceptions}"; strExceptions = $"<local>;{strExceptions}";
} }
strProxy = string.Empty; var strProxy = string.Empty;
if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty()) if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{ {
strProxy = $"{Global.Loopback}:{port}"; strProxy = $"{Global.Loopback}:{port}";
@@ -86,6 +102,8 @@ public static class SysProxyHandler
.Replace("{http_port}", port.ToString()) .Replace("{http_port}", port.ToString())
.Replace("{socks_port}", port.ToString()); .Replace("{socks_port}", port.ToString());
} }
return (strProxy, strExceptions);
} }
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
+17 -6
View File
@@ -183,7 +183,7 @@ public class CoreManager
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true); var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true, context.IsTunEnabled);
if (proc is null) if (proc is null)
{ {
return; return;
@@ -201,7 +201,7 @@ public class CoreManager
if (result.Success) if (result.Success)
{ {
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType); var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType);
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true, preContext.IsTunEnabled);
if (proc is null) if (proc is null)
{ {
return; return;
@@ -289,7 +289,20 @@ public class CoreManager
#region Process #region Process
private async Task<ProcessService?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo) /// <summary>
/// Decides whether a core launch must be elevated on non-Windows platforms.
/// The TUN state comes from the immutable <see cref="CoreConfigContext" /> snapshot that
/// generated the config, never from the live mutable config: the generated config and the
/// launch mode must always agree, even if TUN is toggled while a reload is in flight.
/// </summary>
public static bool ShouldRunAsSudo(bool isTunLaunch, ECoreType? coreType, bool isNonWindows)
{
return isTunLaunch
&& coreType is ECoreType.sing_box or ECoreType.mihomo or ECoreType.Xray
&& isNonWindows;
}
private async Task<ProcessService?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo, bool isTunLaunch = false)
{ {
var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg); var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg);
if (fileName.IsNullOrEmpty()) if (fileName.IsNullOrEmpty())
@@ -301,9 +314,7 @@ public class CoreManager
try try
{ {
if (mayNeedSudo if (mayNeedSudo
&& _config.TunModeItem.EnableTun && ShouldRunAsSudo(isTunLaunch, coreInfo.CoreType, Utils.IsNonWindows()))
&& (coreInfo.CoreType is ECoreType.sing_box or ECoreType.mihomo or ECoreType.Xray)
&& Utils.IsNonWindows())
{ {
_linuxSudo = true; _linuxSudo = true;
await CoreAdminManager.Instance.Init(_config, _updateFunc); await CoreAdminManager.Instance.Init(_config, _updateFunc);
@@ -147,10 +147,10 @@ public class TunModeItem
public int Mtu { get; set; } public int Mtu { get; set; }
public bool EnableIPv6Address { get; set; } public bool EnableIPv6Address { get; set; }
public string IcmpRouting { get; set; } public string IcmpRouting { get; set; }
public bool EnableLegacyProtect { get; set; } public bool EnableLegacyProtect { get; set; } = true;
public List<string>? RouteExcludeAddress { get; set; } public List<string>? RouteExcludeAddress { get; set; }
public string Ipv4Address { get; set; } public string IPv4Address { get; set; }
public string Ipv6Address { get; set; } public string IPv6Address { get; set; }
} }
[Serializable] [Serializable]
@@ -560,6 +560,7 @@ public class Sockopt4Ray
[JsonPropertyName("interface")] [JsonPropertyName("interface")]
public string? Interface { get; set; } public string? Interface { get; set; }
public HappyEyeballs4Ray? happyEyeballs { get; set; } public HappyEyeballs4Ray? happyEyeballs { get; set; }
} }
+26 -25
View File
@@ -1,27 +1,28 @@
{ {
"tag": "tun", "tag": "tun",
"protocol": "tun", "protocol": "tun",
"settings": { "settings": {
"name": "xray_tun", "name": "xray_tun",
"MTU": 9000, "MTU": 9000,
"gateway": [ "gateway": [
"172.18.0.1/30", "172.18.0.1/30",
"fdfe:dcba:9876::1/126" "fdfe:dcba:9876::1/126"
], ],
"dns": [ "dns": [
"172.18.0.1" "1.1.1.1",
], "8.8.8.8"
"autoSystemRoutingTable": [ ],
"0.0.0.0/0", "autoSystemRoutingTable": [
"::/0" "0.0.0.0/0",
], "::/0"
"autoOutboundsInterface": "auto" ],
}, "autoOutboundsInterface": "auto"
"sniffing": { },
"enabled": true, "sniffing": {
"destOverride": [ "enabled": true,
"http", "destOverride": [
"tls" "http",
] "tls"
} ]
}
} }
@@ -68,11 +68,11 @@ public partial class CoreConfigSingboxService
tunInbound.strict_route = _config.TunModeItem.StrictRoute; tunInbound.strict_route = _config.TunModeItem.StrictRoute;
tunInbound.stack = _config.TunModeItem.Stack; tunInbound.stack = _config.TunModeItem.Stack;
var address = _config.TunModeItem.Ipv4Address.NullIfEmpty() ?? Global.TunIpv4Address.First(); var address = _config.TunModeItem.IPv4Address.NullIfEmpty() ?? Global.TunIPv4Address.First();
tunInbound.address = [address]; tunInbound.address = [address];
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.address.Add(address6); tunInbound.address.Add(address6);
} }
tunInbound.route_exclude_address = _config.TunModeItem.RouteExcludeAddress; tunInbound.route_exclude_address = _config.TunModeItem.RouteExcludeAddress;
@@ -64,14 +64,14 @@ public partial class CoreConfigV2rayService
tunInbound.settings.name = context.IsMacOS ? $"utun{new Random().Next(99)}" : "xray_tun"; tunInbound.settings.name = context.IsMacOS ? $"utun{new Random().Next(99)}" : "xray_tun";
tunInbound.settings.MTU = _config.TunModeItem.Mtu; tunInbound.settings.MTU = _config.TunModeItem.Mtu;
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];
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.dns = [address.Split('/').First()];
tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0"]; tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0"];
var bindInterface = _config.CoreBasicItem.BindInterface?.TrimEx(); var bindInterface = _config.CoreBasicItem.BindInterface?.TrimEx();
if (!bindInterface.IsNullOrEmpty()) if (!bindInterface.IsNullOrEmpty())
@@ -3,6 +3,7 @@ namespace ServiceLib.ViewModels;
public class AddServer2ViewModel : MyReactiveObject, ICloseable public class AddServer2ViewModel : MyReactiveObject, ICloseable
{ {
public event EventHandler? RequestClose; public event EventHandler? RequestClose;
public Interaction<Unit, string?> BrowseConfigFileInteraction { get; } = new(); public Interaction<Unit, string?> BrowseConfigFileInteraction { get; } = new();
[Reactive] [Reactive]
@@ -95,8 +95,8 @@ public class OptionSettingViewModel : MyReactiveObject, ICloseable
[Reactive] public string TunIcmpRouting { get; set; } [Reactive] public string TunIcmpRouting { get; set; }
[Reactive] public bool TunEnableLegacyProtect { get; set; } [Reactive] public bool TunEnableLegacyProtect { get; set; }
[Reactive] public string TunRouteExcludeAddress { get; set; } [Reactive] public string TunRouteExcludeAddress { get; set; }
[Reactive] public string TunIpv4Address { get; set; } [Reactive] public string TunIPv4Address { get; set; }
[Reactive] public string TunIpv6Address { get; set; } [Reactive] public string TunIPv6Address { get; set; }
#endregion Tun mode #endregion Tun mode
@@ -216,8 +216,8 @@ public class OptionSettingViewModel : MyReactiveObject, ICloseable
TunIcmpRouting = _config.TunModeItem.IcmpRouting; TunIcmpRouting = _config.TunModeItem.IcmpRouting;
TunEnableLegacyProtect = _config.TunModeItem.EnableLegacyProtect; TunEnableLegacyProtect = _config.TunModeItem.EnableLegacyProtect;
TunRouteExcludeAddress = Utils.List2String(_config.TunModeItem.RouteExcludeAddress, true); TunRouteExcludeAddress = Utils.List2String(_config.TunModeItem.RouteExcludeAddress, true);
TunIpv4Address = _config.TunModeItem.Ipv4Address; TunIPv4Address = _config.TunModeItem.IPv4Address;
TunIpv6Address = _config.TunModeItem.Ipv6Address; TunIPv6Address = _config.TunModeItem.IPv6Address;
#endregion Tun mode #endregion Tun mode
@@ -382,8 +382,8 @@ public class OptionSettingViewModel : MyReactiveObject, ICloseable
_config.TunModeItem.IcmpRouting = TunIcmpRouting; _config.TunModeItem.IcmpRouting = TunIcmpRouting;
_config.TunModeItem.EnableLegacyProtect = TunEnableLegacyProtect; _config.TunModeItem.EnableLegacyProtect = TunEnableLegacyProtect;
_config.TunModeItem.RouteExcludeAddress = Utils.String2List(TunRouteExcludeAddress); _config.TunModeItem.RouteExcludeAddress = Utils.String2List(TunRouteExcludeAddress);
_config.TunModeItem.Ipv4Address = TunIpv4Address; _config.TunModeItem.IPv4Address = TunIPv4Address;
_config.TunModeItem.Ipv6Address = TunIpv6Address; _config.TunModeItem.IPv6Address = TunIPv6Address;
//coreType //coreType
await SaveCoreType(); await SaveCoreType();
@@ -3,6 +3,7 @@ namespace ServiceLib.ViewModels;
public class ProfilesSelectViewModel : MyReactiveObject, ICloseable public class ProfilesSelectViewModel : MyReactiveObject, ICloseable
{ {
public event EventHandler? RequestClose; public event EventHandler? RequestClose;
public Interaction<Unit, Unit> ProfilesFocusInteraction { get; } = new(); public Interaction<Unit, Unit> ProfilesFocusInteraction { get; } = new();
#region private prop #region private prop
@@ -3,6 +3,7 @@ namespace ServiceLib.ViewModels;
public class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable public class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable
{ {
public event EventHandler? RequestClose; public event EventHandler? RequestClose;
public Interaction<string, bool> ShowYesNoInteraction { get; } = new(); public Interaction<string, bool> ShowYesNoInteraction { get; } = new();
public Interaction<string, Unit> SetClipboardDataInteraction { get; } = new(); public Interaction<string, Unit> SetClipboardDataInteraction { get; } = new();
public Interaction<Unit, string?> ReadTextFromClipboardInteraction { get; } = new(); public Interaction<Unit, string?> ReadTextFromClipboardInteraction { get; } = new();
@@ -33,8 +33,8 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
cmbMtu.ItemsSource = Global.TunMtus; cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks; cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies; cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbIpv4Address.ItemsSource = Global.TunIpv4Address; cmbIpv4Address.ItemsSource = Global.TunIPv4Address;
cmbIpv6Address.ItemsSource = Global.TunIpv6Address; cmbIpv6Address.ItemsSource = Global.TunIPv6Address;
cmbFragmentPackets.ItemsSource = Global.FragmentPacketsOptions; cmbFragmentPackets.ItemsSource = Global.FragmentPacketsOptions;
cmbCoreType1.ItemsSource = Global.CoreTypes; cmbCoreType1.ItemsSource = Global.CoreTypes;
@@ -131,8 +131,8 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).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.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIpv4Address, v => v.cmbIpv4Address.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunIPv4Address, v => v.cmbIpv4Address.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIpv6Address, v => v.cmbIpv6Address.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunIPv6Address, v => v.cmbIpv6Address.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).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); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);
@@ -29,8 +29,8 @@ public partial class OptionSettingWindow
cmbMtu.ItemsSource = Global.TunMtus; cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks; cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies; cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbIpv4Address.ItemsSource = Global.TunIpv4Address; cmbIpv4Address.ItemsSource = Global.TunIPv4Address;
cmbIpv6Address.ItemsSource = Global.TunIpv6Address; cmbIpv6Address.ItemsSource = Global.TunIPv6Address;
cmbFragmentPackets.ItemsSource = Global.FragmentPacketsOptions; cmbFragmentPackets.ItemsSource = Global.FragmentPacketsOptions;
cmbCoreType1.ItemsSource = Global.CoreTypes; cmbCoreType1.ItemsSource = Global.CoreTypes;
@@ -126,8 +126,8 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.Text).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.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIpv4Address, v => v.cmbIpv4Address.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunIPv4Address, v => v.cmbIpv4Address.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIpv6Address, v => v.cmbIpv6Address.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunIPv6Address, v => v.cmbIpv6Address.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).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); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.Text).DisposeWith(disposables);