Compare commits

..

4 Commits

Author SHA1 Message Date
DHR60 50023a5976 Xray tun features (#10264)
* Xray tun features

* Try sync
2026-10-01 14:55:09 +08:00
DHR60 c659ca8af8 Add MASQUE support (#10233)
* Add MASQUE support

* Add SettingsCoreType
2026-10-01 14:49:45 +08:00
2dust 7d6a967c18 up 7.25.4 2026-09-30 14:38:34 +08:00
2dust 6c2edcb382 Revert "Bump Avalonia.Desktop from 12.1.2 to 12.1.3 (#10216)"
This reverts commit e1cb99cd6e.
2026-09-30 14:34:07 +08:00
32 changed files with 412 additions and 15 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.25.3</Version> <Version>7.25.4</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
+1 -1
View File
@@ -7,7 +7,7 @@
<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.2" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
<PackageVersion Include="Avalonia.Desktop" Version="12.1.3" /> <PackageVersion Include="Avalonia.Desktop" Version="12.1.2" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" /> <PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
<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" />
+25
View File
@@ -501,6 +501,31 @@ public class Utils
}; };
} }
public static List<(string, string)> ParseHeaders(string? headers)
{
var result = new List<(string, string)>();
if (headers.IsNullOrEmpty())
{
return result;
}
var lines = headers.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var parts = line.Split(new[] { ':' }, 2);
if (parts.Length != 2)
{
continue;
}
var key = parts[0].Trim();
var value = parts[1].Trim();
if (!string.IsNullOrEmpty(key))
{
result.Add((key, value));
}
}
return result;
}
#endregion Conversion Functions #endregion Conversion Functions
#region Data Checks #region Data Checks
+1
View File
@@ -15,6 +15,7 @@ public enum EConfigType
Anytls = 11, Anytls = 11,
Naive = 12, Naive = 12,
Outbound = 13, Outbound = 13,
MASQUE = 14,
PolicyGroup = 101, PolicyGroup = 101,
ProxyChain = 102, ProxyChain = 102,
} }
+4 -1
View File
@@ -261,7 +261,8 @@ public class Global
{ EConfigType.TUIC, "tuic" }, { EConfigType.TUIC, "tuic" },
{ EConfigType.WireGuard, "wireguard" }, { EConfigType.WireGuard, "wireguard" },
{ EConfigType.Anytls, "anytls" }, { EConfigType.Anytls, "anytls" },
{ EConfigType.Naive, "naive" } { EConfigType.Naive, "naive" },
{ EConfigType.MASQUE, "masque" },
}; };
public static readonly List<string> VmessSecurities = public static readonly List<string> VmessSecurities =
@@ -371,6 +372,7 @@ public class Global
EConfigType.Trojan, EConfigType.Trojan,
EConfigType.Hysteria2, EConfigType.Hysteria2,
EConfigType.WireGuard, EConfigType.WireGuard,
EConfigType.MASQUE,
EConfigType.SOCKS, EConfigType.SOCKS,
EConfigType.HTTP, EConfigType.HTTP,
]; ];
@@ -386,6 +388,7 @@ public class Global
EConfigType.Anytls, EConfigType.Anytls,
EConfigType.Naive, EConfigType.Naive,
EConfigType.WireGuard, EConfigType.WireGuard,
EConfigType.MASQUE,
EConfigType.SOCKS, EConfigType.SOCKS,
EConfigType.HTTP, EConfigType.HTTP,
]; ];
@@ -49,6 +49,7 @@ public class CoreConfigContextBuilder
RoutingItem = await ConfigHandler.GetDefaultRouting(config), RoutingItem = await ConfigHandler.GetDefaultRouting(config),
IsWindows = Utils.IsWindows(), IsWindows = Utils.IsWindows(),
IsMacOS = Utils.IsMacOS(), IsMacOS = Utils.IsMacOS(),
IsLinux = Utils.IsLinux(),
HasGlobalIPv6Address = Utils.HasGlobalIPv6Address(), HasGlobalIPv6Address = Utils.HasGlobalIPv6Address(),
ProtectCoreTypeList = config.TunModeItem.EnableTun ? [ECoreType.Xray, ECoreType.sing_box] : [] ProtectCoreTypeList = config.TunModeItem.EnableTun ? [ECoreType.Xray, ECoreType.sing_box] : []
}; };
@@ -292,6 +292,7 @@ public static class ConfigHandler
EConfigType.WireGuard => await AddWireguardServer(config, item), EConfigType.WireGuard => await AddWireguardServer(config, item),
EConfigType.Anytls => await AddAnytlsServer(config, item), EConfigType.Anytls => await AddAnytlsServer(config, item),
EConfigType.Naive => await AddNaiveServer(config, item), EConfigType.Naive => await AddNaiveServer(config, item),
EConfigType.MASQUE => await AddMasqueServer(config, item),
_ => -1, _ => -1,
}; };
return ret; return ret;
@@ -992,6 +993,29 @@ public static class ConfigHandler
return 0; return 0;
} }
public static async Task<int> AddMasqueServer(Config config, ProfileItem profileItem, bool toFile = true)
{
profileItem.ConfigType = EConfigType.MASQUE;
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Username = profileItem.Username.TrimEx();
profileItem.Password = profileItem.Password.TrimEx();
profileItem.Fingerprint = string.Empty;
profileItem.Alpn = string.Empty;
profileItem.Network = string.Empty;
profileItem.AllowInsecure = string.Empty;
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
profileItem.StreamSecurity = Global.StreamSecurity;
}
if (profileItem.Password.IsNullOrEmpty())
{
return -1;
}
await AddServerCommon(config, profileItem, toFile);
return 0;
}
/// <summary> /// <summary>
/// Sort the server list by the specified column /// Sort the server list by the specified column
/// Updates the sort order in the profile extension data /// Updates the sort order in the profile extension data
@@ -1684,6 +1708,7 @@ public static class ConfigHandler
EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false), EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false),
EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false), EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false),
EConfigType.Naive => await AddNaiveServer(config, profileItem, false), EConfigType.Naive => await AddNaiveServer(config, profileItem, false),
EConfigType.MASQUE => await AddMasqueServer(config, profileItem, false),
_ => -1, _ => -1,
}; };
@@ -24,6 +24,7 @@ public record CoreConfigContext
public bool IsWindows { get; init; } public bool IsWindows { get; init; }
public bool IsMacOS { get; init; } public bool IsMacOS { get; init; }
public bool IsLinux { get; init; }
// Defaults to true so that a context built without this flag keeps routing IPv6 into the // Defaults to true so that a context built without this flag keeps routing IPv6 into the
// tunnel; only a positive detection of the host having no global IPv6 address turns it off. // tunnel; only a positive detection of the host having no global IPv6 address turns it off.
@@ -128,8 +128,6 @@ public class User4Sbox
public class Outbound4Sbox : BaseServer4Sbox public class Outbound4Sbox : BaseServer4Sbox
{ {
public string? server { get; set; }
public int? server_port { get; set; }
public List<string>? server_ports { get; set; } public List<string>? server_ports { get; set; }
public string? uuid { get; set; } public string? uuid { get; set; }
public string? security { get; set; } public string? security { get; set; }
@@ -171,6 +169,10 @@ public class Endpoints4Sbox : BaseServer4Sbox
public string? udp_timeout { get; set; } public string? udp_timeout { get; set; }
public int? workers { get; set; } public int? workers { get; set; }
public List<Peer4Sbox> peers { get; set; } public List<Peer4Sbox> peers { get; set; }
public string? username { get; set; }
public string? password { get; set; }
public string? path { get; set; }
public Dictionary<string, List<string>>? headers { get; set; }
} }
public class Peer4Sbox public class Peer4Sbox
@@ -271,10 +273,8 @@ public class Server4Sbox : BaseServer4Sbox
public string? inet4_range { get; set; } public string? inet4_range { get; set; }
public string? inet6_range { get; set; } public string? inet6_range { get; set; }
public string? client_subnet { get; set; } public string? client_subnet { get; set; }
public string? server { get; set; }
public new string? domain_resolver { get; set; } public new string? domain_resolver { get; set; }
[JsonPropertyName("interface")] public string? Interface { get; set; } [JsonPropertyName("interface")] public string? Interface { get; set; }
public int? server_port { get; set; }
public string? path { get; set; } public string? path { get; set; }
public Headers4Sbox? headers { get; set; } public Headers4Sbox? headers { get; set; }
@@ -359,4 +359,6 @@ public abstract class BaseServer4Sbox : DialFields4Sbox
{ {
public string type { get; set; } public string type { get; set; }
public string tag { get; set; } public string tag { get; set; }
public string? server { get; set; }
public int? server_port { get; set; }
} }
@@ -92,6 +92,10 @@ public class Inboundsettings4Ray
public string? autoOutboundsInterface { get; set; } public string? autoOutboundsInterface { get; set; }
public List<string>? dns { get; set; } public List<string>? dns { get; set; }
public bool? autoSystemDnsToGateway { get; set; }
public List<string>? autoSystemWfpBlockLeak { get; set; }
} }
public class Sniffing4Ray public class Sniffing4Ray
@@ -315,6 +319,7 @@ public class StreamSettings4Ray
public GrpcSettings4Ray? grpcSettings { get; set; } public GrpcSettings4Ray? grpcSettings { get; set; }
public HysteriaSettings4Ray? hysteriaSettings { get; set; } public HysteriaSettings4Ray? hysteriaSettings { get; set; }
public MasqueSettings4Ray? masqueSettings { get; set; }
public object? finalmask { get; set; } public object? finalmask { get; set; }
@@ -443,6 +448,12 @@ public class HysteriaSettings4Ray
public string? auth { get; set; } public string? auth { get; set; }
} }
public class MasqueSettings4Ray
{
public string? path { get; set; }
public Dictionary<string, string>? headers { get; set; }
}
public class UdpHop4Ray public class UdpHop4Ray
{ {
public string? ports { get; set; } public string? ports { get; set; }
@@ -46,6 +46,10 @@ public record ProtocolExtraItem
public int? InsecureConcurrency { get; init; } public int? InsecureConcurrency { get; init; }
public bool? NaiveQuic { get; init; } public bool? NaiveQuic { get; init; }
// MASQUE
public string? MasquePath { get; init; }
public string? MasqueHeaders { get; init; }
// group profile // group profile
public string? GroupType { get; init; } public string? GroupType { get; init; }
public string? ChildItems { get; init; } public string? ChildItems { get; init; }
+9
View File
@@ -789,6 +789,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Add [MASQUE] 的本地化字符串。
/// </summary>
public static string menuAddMasqueServer {
get {
return ResourceManager.GetString("menuAddMasqueServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Add [NaïveProxy] 的本地化字符串。 /// 查找类似 Add [NaïveProxy] 的本地化字符串。
/// </summary> /// </summary>
+3
View File
@@ -1899,4 +1899,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbSortingProcess" xml:space="preserve"> <data name="TbSortingProcess" xml:space="preserve">
<value>Process</value> <value>Process</value>
</data> </data>
<data name="menuAddMasqueServer" xml:space="preserve">
<value>Add [MASQUE]</value>
</data>
</root> </root>
@@ -1891,4 +1891,7 @@
<data name="TbSortingProcess" xml:space="preserve"> <data name="TbSortingProcess" xml:space="preserve">
<value>进程</value> <value>进程</value>
</data> </data>
<data name="menuAddMasqueServer" xml:space="preserve">
<value>添加 [MASQUE]</value>
</data>
</root> </root>
@@ -77,7 +77,7 @@ public partial class CoreConfigSingboxService
} }
} }
if (_node.ConfigType == EConfigType.WireGuard) if (_node.ConfigType is EConfigType.WireGuard or EConfigType.MASQUE)
{ {
var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound); var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound);
FillEndpoint(endpoint); FillEndpoint(endpoint);
@@ -350,13 +350,13 @@ public partial class CoreConfigSingboxService
{ {
var protocolExtra = _node.GetProtocolExtra(); var protocolExtra = _node.GetProtocolExtra();
endpoint.address = Utils.String2List(protocolExtra.WgInterfaceAddress)?.Select(s => s.Trim()).ToList() ?? ["172.16.0.2/32"];
endpoint.type = Global.ProtocolTypes[_node.ConfigType]; endpoint.type = Global.ProtocolTypes[_node.ConfigType];
switch (_node.ConfigType) switch (_node.ConfigType)
{ {
case EConfigType.WireGuard: case EConfigType.WireGuard:
{ {
endpoint.address = Utils.String2List(protocolExtra.WgInterfaceAddress)?.Select(s => s.Trim()).ToList() ?? ["172.16.0.2/32"];
var peer = new Peer4Sbox var peer = new Peer4Sbox
{ {
public_key = protocolExtra.WgPublicKey ?? string.Empty, public_key = protocolExtra.WgPublicKey ?? string.Empty,
@@ -371,6 +371,28 @@ public partial class CoreConfigSingboxService
endpoint.peers = [peer]; endpoint.peers = [peer];
break; break;
} }
case EConfigType.MASQUE:
{
endpoint.type = "masque-client";
endpoint.server = _node.Address;
endpoint.server_port = _node.Port;
endpoint.username = _node.Username;
endpoint.password = _node.Password;
endpoint.path = protocolExtra?.MasquePath.NullIfEmpty();
if (!string.IsNullOrEmpty(protocolExtra?.MasqueHeaders))
{
var headers = Utils.ParseHeaders(protocolExtra.MasqueHeaders)
.GroupBy(x => x.Item1, StringComparer.OrdinalIgnoreCase)
.ToDictionary(
g => g.Key,
g => g.Select(x => x.Item2).ToList(),
StringComparer.OrdinalIgnoreCase
);
endpoint.headers = headers.Count > 0 ? headers : null;
}
FillOutboundTls(endpoint);
break;
}
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -402,7 +424,7 @@ public partial class CoreConfigSingboxService
} }
} }
private void FillOutboundTls(Outbound4Sbox outbound) private void FillOutboundTls(BaseServer4Sbox server)
{ {
try try
{ {
@@ -476,7 +498,7 @@ public partial class CoreConfigSingboxService
{ {
tls.ech = ech; tls.ech = ech;
} }
outbound.tls = tls; server.tls = tls;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -90,6 +90,15 @@ public partial class CoreConfigV2rayService
// tunInbound.sniffing.routeOnly = inbound.sniffing.routeOnly; // tunInbound.sniffing.routeOnly = inbound.sniffing.routeOnly;
tunInbound.sniffing.routeOnly = true; tunInbound.sniffing.routeOnly = true;
if (context.IsWindows && _config.TunModeItem.StrictRoute == true)
{
tunInbound.settings.autoSystemWfpBlockLeak = ["dns", "misconfigtun"];
}
if (context.IsLinux)
{
tunInbound.settings.autoSystemDnsToGateway = true;
}
if (_config.TunModeItem.RouteExcludeAddress is { Count: > 0 }) if (_config.TunModeItem.RouteExcludeAddress is { Count: > 0 })
{ {
var wholeInternet = IPNetwork2.Parse("0.0.0.0/0"); var wholeInternet = IPNetwork2.Parse("0.0.0.0/0");
@@ -182,6 +182,12 @@ public partial class CoreConfigV2rayService
outboundSettings.version = 2; outboundSettings.version = 2;
break; break;
} }
case EConfigType.MASQUE:
{
outboundSettings.address = _node.Address;
outboundSettings.port = _node.Port;
break;
}
case EConfigType.WireGuard: case EConfigType.WireGuard:
{ {
var address = _node.Address; var address = _node.Address;
@@ -257,8 +263,13 @@ public partial class CoreConfigV2rayService
{ {
network = "hysteria"; network = "hysteria";
} }
else if (_node.ConfigType == EConfigType.MASQUE)
{
network = "masque";
}
streamSettings.network = network; streamSettings.network = network;
var transport = _node.GetTransportExtra(); var transport = _node.GetTransportExtra();
var protocolExtra = _node.GetProtocolExtra();
var host = string.Empty; var host = string.Empty;
var path = string.Empty; var path = string.Empty;
var kcpSeed = string.Empty; var kcpSeed = string.Empty;
@@ -502,7 +513,6 @@ public partial class CoreConfigV2rayService
break; break;
case "hysteria": case "hysteria":
var protocolExtra = _node.GetProtocolExtra();
var ports = protocolExtra?.Ports; var ports = protocolExtra?.Ports;
int? upMbps = protocolExtra?.UpMbps is { } su and >= 0 int? upMbps = protocolExtra?.UpMbps is { } su and >= 0
? su ? su
@@ -574,6 +584,32 @@ public partial class CoreConfigV2rayService
streamSettings.finalmask = hy2Finalmask; streamSettings.finalmask = hy2Finalmask;
break; break;
case "masque":
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var authValue = $"Basic {Utils.Base64Encode($"{_node.Username}:{_node.Password}")}";
headers.Add("Authorization", authValue);
if (!string.IsNullOrEmpty(protocolExtra?.MasqueHeaders))
{
var userHeaders = Utils.ParseHeaders(protocolExtra.MasqueHeaders)
.GroupBy(x => x.Item1, StringComparer.OrdinalIgnoreCase)
.ToDictionary(
g => g.Key,
g => string.Join(", ", g.Select(x => x.Item2)),
StringComparer.OrdinalIgnoreCase
);
if (userHeaders.TryGetValue("Authorization", out var userAuthValue))
{
headers["Authorization"] = userAuthValue;
}
headers = userHeaders;
}
streamSettings.masqueSettings = new()
{
path = protocolExtra?.MasquePath.NullIfEmpty(),
headers = headers.Count > 0 ? headers : null,
};
break;
default: default:
// raw // raw
if (headerType == Global.RawHeaderHttp) if (headerType == Global.RawHeaderHttp)
@@ -97,6 +97,12 @@ public partial class AddServerViewModel : MyReactiveObject, ICloseable
[Reactive] [Reactive]
public partial int GeckoMaxPacketSize { get; set; } public partial int GeckoMaxPacketSize { get; set; }
[Reactive]
public partial string MasquePath { get; set; }
[Reactive]
public partial string MasqueHeaders { get; set; }
[Reactive] [Reactive]
public partial string RawHeaderType { get; set; } public partial string RawHeaderType { get; set; }
@@ -322,6 +328,8 @@ public partial class AddServerViewModel : MyReactiveObject, ICloseable
Hy2RealmUrl = protocolExtra.Hy2RealmUrl ?? string.Empty; Hy2RealmUrl = protocolExtra.Hy2RealmUrl ?? string.Empty;
GeckoMinPacketSize = protocolExtra.GeckoMinPacketSize.ToInt(); GeckoMinPacketSize = protocolExtra.GeckoMinPacketSize.ToInt();
GeckoMaxPacketSize = protocolExtra.GeckoMaxPacketSize.ToInt(); GeckoMaxPacketSize = protocolExtra.GeckoMaxPacketSize.ToInt();
MasquePath = protocolExtra.MasquePath ?? string.Empty;
MasqueHeaders = protocolExtra.MasqueHeaders ?? string.Empty;
RawHeaderType = transport.RawHeaderType ?? Global.None; RawHeaderType = transport.RawHeaderType ?? Global.None;
Host = transport.Host ?? string.Empty; Host = transport.Host ?? string.Empty;
@@ -443,6 +451,8 @@ public partial class AddServerViewModel : MyReactiveObject, ICloseable
Hy2RealmUrl = realm?.ToUri().NullIfEmpty(), Hy2RealmUrl = realm?.ToUri().NullIfEmpty(),
GeckoMinPacketSize = GeckoMinPacketSize > 0 ? GeckoMinPacketSize.ToString() : null, GeckoMinPacketSize = GeckoMinPacketSize > 0 ? GeckoMinPacketSize.ToString() : null,
GeckoMaxPacketSize = GeckoMaxPacketSize > 0 ? GeckoMaxPacketSize.ToString() : null, GeckoMaxPacketSize = GeckoMaxPacketSize > 0 ? GeckoMaxPacketSize.ToString() : null,
MasquePath = MasquePath.NullIfEmpty(),
MasqueHeaders = MasqueHeaders.NullIfEmpty(),
}); });
SelectedSource.SetTransportExtra(transport); SelectedSource.SetTransportExtra(transport);
@@ -32,6 +32,7 @@ public partial class MainWindowViewModel : MyReactiveObject
public ReactiveCommand<RxVoid, RxVoid> AddWireguardServerCmd { get; } public ReactiveCommand<RxVoid, RxVoid> AddWireguardServerCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> AddAnytlsServerCmd { get; } public ReactiveCommand<RxVoid, RxVoid> AddAnytlsServerCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> AddNaiveServerCmd { get; } public ReactiveCommand<RxVoid, RxVoid> AddNaiveServerCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> AddMasqueServerCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> AddCustomServerCmd { get; } public ReactiveCommand<RxVoid, RxVoid> AddCustomServerCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> AddCustomOutboundServerCmd { get; } public ReactiveCommand<RxVoid, RxVoid> AddCustomOutboundServerCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> AddPolicyGroupServerCmd { get; } public ReactiveCommand<RxVoid, RxVoid> AddPolicyGroupServerCmd { get; }
@@ -140,6 +141,10 @@ public partial class MainWindowViewModel : MyReactiveObject
{ {
await AddServerAsync(EConfigType.Naive); await AddServerAsync(EConfigType.Naive);
}); });
AddMasqueServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerAsync(EConfigType.MASQUE);
});
AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () => AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await AddServerAsync(EConfigType.Custom); await AddServerAsync(EConfigType.Custom);
@@ -113,6 +113,7 @@ public partial class OptionSettingViewModel : MyReactiveObject, ICloseable
[Reactive] public partial string CoreType6 { get; set; } [Reactive] public partial string CoreType6 { get; set; }
[Reactive] public partial string CoreType7 { get; set; } [Reactive] public partial string CoreType7 { get; set; }
[Reactive] public partial string CoreType9 { get; set; } [Reactive] public partial string CoreType9 { get; set; }
[Reactive] public partial string CoreType14 { get; set; }
#endregion CoreType #endregion CoreType
@@ -283,6 +284,10 @@ public partial class OptionSettingViewModel : MyReactiveObject, ICloseable
case 9: case 9:
CoreType9 = type; CoreType9 = type;
break; break;
case 14:
CoreType14 = type;
break;
} }
}); });
await Task.CompletedTask; await Task.CompletedTask;
@@ -451,6 +456,10 @@ public partial class OptionSettingViewModel : MyReactiveObject, ICloseable
type = CoreType9; type = CoreType9;
break; break;
case 14:
type = CoreType14;
break;
default: default:
continue; continue;
} }
@@ -783,6 +783,70 @@
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
</Grid> </Grid>
<Grid
x:Name="gridMasque"
Grid.Row="2"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUsername}" />
<TextBox
x:Name="txtId14"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtSecurity14"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPath14"
Grid.Row="3"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHttpOutboundHeaders}" />
<TextBox
x:Name="txtHttpHeaders14"
Grid.Row="4"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
AcceptsReturn="True"
Classes="TextArea"
PlaceholderText="Header: Value"
TextWrapping="NoWrap" />
</Grid>
<Separator <Separator
x:Name="sepa2" x:Name="sepa2"
Grid.Row="3" Grid.Row="3"
@@ -141,6 +141,14 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(currentTypeDisposables); this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(currentTypeDisposables); this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(currentTypeDisposables);
break; break;
case EConfigType.MASQUE:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId14.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity14.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.MasquePath, v => v.txtPath14.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.MasqueHeaders, v => v.txtHttpHeaders14.Text).DisposeWith(currentTypeDisposables);
break;
} }
}) })
.DisposeWith(disposables); .DisposeWith(disposables);
@@ -284,6 +292,14 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls; cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
break; break;
case EConfigType.MASQUE:
gridMasque.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbFingerprint.IsEnabled = false;
cmbAlpn.IsEnabled = false;
break;
} }
cmbStreamSecurity.ItemsSource = lstStreamSecurity; cmbStreamSecurity.ItemsSource = lstStreamSecurity;
cmbStreamSecurity.SelectedItem = profileItem.StreamSecurity; cmbStreamSecurity.SelectedItem = profileItem.StreamSecurity;
@@ -45,6 +45,7 @@
<MenuItem x:Name="menuAddTrojanServer" Header="{x:Static resx:ResUI.menuAddTrojanServer}" /> <MenuItem x:Name="menuAddTrojanServer" Header="{x:Static resx:ResUI.menuAddTrojanServer}" />
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" /> <MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
<MenuItem x:Name="menuAddWireguardServer" Header="{x:Static resx:ResUI.menuAddWireguardServer}" /> <MenuItem x:Name="menuAddWireguardServer" Header="{x:Static resx:ResUI.menuAddWireguardServer}" />
<MenuItem x:Name="menuAddMasqueServer" Header="{x:Static resx:ResUI.menuAddMasqueServer}" />
<MenuItem x:Name="menuAddSocksServer" Header="{x:Static resx:ResUI.menuAddSocksServer}" /> <MenuItem x:Name="menuAddSocksServer" Header="{x:Static resx:ResUI.menuAddSocksServer}" />
<MenuItem x:Name="menuAddHttpServer" Header="{x:Static resx:ResUI.menuAddHttpServer}" /> <MenuItem x:Name="menuAddHttpServer" Header="{x:Static resx:ResUI.menuAddHttpServer}" />
<Separator /> <Separator />
@@ -46,6 +46,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddMasqueServerCmd, v => v.menuAddMasqueServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomOutboundServerCmd, v => v.menuAddCustomOutboundServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomOutboundServerCmd, v => v.menuAddCustomOutboundServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
@@ -1192,7 +1192,7 @@
<Grid <Grid
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
@@ -1296,6 +1296,19 @@
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="9"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="MASQUE" />
<ComboBox
x:Name="cmbCoreType14"
Grid.Row="9"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
</Grid> </Grid>
</TabItem> </TabItem>
</TabControl> </TabControl>
@@ -46,6 +46,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
cmbCoreType6.ItemsSource = Global.CoreTypes; cmbCoreType6.ItemsSource = Global.CoreTypes;
cmbCoreType7.ItemsSource = Global.CoreTypes; cmbCoreType7.ItemsSource = Global.CoreTypes;
cmbCoreType9.ItemsSource = Global.CoreTypes; cmbCoreType9.ItemsSource = Global.CoreTypes;
cmbCoreType14.ItemsSource = Global.CoreTypes;
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(Global.SpeedTestConcurrencyCountMin, 20).ToList(); cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(Global.SpeedTestConcurrencyCountMin, 20).ToList();
cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList();
@@ -145,6 +146,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType14, v => v.cmbCoreType14.SelectedValue).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });
+83
View File
@@ -1000,6 +1000,89 @@
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
</Grid> </Grid>
<Grid
x:Name="gridMasque"
Grid.Row="2"
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbUsername}" />
<TextBox
x:Name="txtId14"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtSecurity14"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPath14"
Grid.Row="3"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHttpOutboundHeaders}" />
<TextBox
x:Name="txtHeaders14"
Grid.Row="4"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
materialDesign:HintAssist.Hint="Header: Value"
AcceptsReturn="True"
MaxLines="12"
MinLines="3"
Style="{StaticResource MyOutlinedTextBox}"
TextWrapping="Wrap" />
</Grid>
<Separator <Separator
x:Name="sepa2" x:Name="sepa2"
Grid.Row="3" Grid.Row="3"
@@ -139,6 +139,13 @@ public partial class AddServerWindow
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(currentTypeDisposables); this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(currentTypeDisposables); this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(currentTypeDisposables);
break; break;
case EConfigType.MASQUE:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId14.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity14.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.MasquePath, v => v.txtPath14.Text).DisposeWith(currentTypeDisposables);
this.Bind(ViewModel, vm => vm.MasqueHeaders, v => v.txtHeaders14.Text).DisposeWith(currentTypeDisposables);
break;
} }
}) })
.DisposeWith(disposables); .DisposeWith(disposables);
@@ -284,6 +291,14 @@ public partial class AddServerWindow
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls; cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
break; break;
case EConfigType.MASQUE:
gridMasque.Visibility = Visibility.Visible;
sepa2.Visibility = Visibility.Collapsed;
gridTransport.Visibility = Visibility.Collapsed;
cmbFingerprint.IsEnabled = false;
cmbAlpn.IsEnabled = false;
break;
} }
cmbStreamSecurity.ItemsSource = lstStreamSecurity; cmbStreamSecurity.ItemsSource = lstStreamSecurity;
cmbStreamSecurity.SelectedItem = profileItem.StreamSecurity; cmbStreamSecurity.SelectedItem = profileItem.StreamSecurity;
+4
View File
@@ -112,6 +112,10 @@
x:Name="menuAddWireguardServer" x:Name="menuAddWireguardServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddWireguardServer}" /> Header="{x:Static resx:ResUI.menuAddWireguardServer}" />
<MenuItem
x:Name="menuAddMasqueServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddMasqueServer}" />
<MenuItem <MenuItem
x:Name="menuAddSocksServer" x:Name="menuAddSocksServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
+1
View File
@@ -46,6 +46,7 @@ public partial class MainWindow
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddMasqueServerCmd, v => v.menuAddMasqueServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomOutboundServerCmd, v => v.menuAddCustomOutboundServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomOutboundServerCmd, v => v.menuAddCustomOutboundServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
@@ -1473,6 +1473,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@@ -1597,6 +1598,21 @@
Width="200" Width="200"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
Style="{StaticResource DefComboBox}" /> Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="9"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="MASQUE" />
<ComboBox
x:Name="cmbCoreType14"
Grid.Row="9"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
Style="{StaticResource DefComboBox}" />
</Grid> </Grid>
</TabItem> </TabItem>
</TabControl> </TabControl>
@@ -42,6 +42,7 @@ public partial class OptionSettingWindow
cmbCoreType6.ItemsSource = Global.CoreTypes; cmbCoreType6.ItemsSource = Global.CoreTypes;
cmbCoreType7.ItemsSource = Global.CoreTypes; cmbCoreType7.ItemsSource = Global.CoreTypes;
cmbCoreType9.ItemsSource = Global.CoreTypes; cmbCoreType9.ItemsSource = Global.CoreTypes;
cmbCoreType14.ItemsSource = Global.CoreTypes;
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(Global.SpeedTestConcurrencyCountMin, 20).ToList(); cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(Global.SpeedTestConcurrencyCountMin, 20).ToList();
cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList();
@@ -141,6 +142,7 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType14, v => v.cmbCoreType14.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });