Add Finalmask support

This commit is contained in:
DHR60 2026-02-19 01:14:29 +08:00
parent b5800f7dfc
commit b1b7fdb1f5
17 changed files with 137 additions and 14 deletions

View file

@ -254,6 +254,7 @@ public static class ConfigHandler
item.CertSha = profileItem.CertSha; item.CertSha = profileItem.CertSha;
item.EchConfigList = profileItem.EchConfigList; item.EchConfigList = profileItem.EchConfigList;
item.EchForceQuery = profileItem.EchForceQuery; item.EchForceQuery = profileItem.EchForceQuery;
item.Finalmask = profileItem.Finalmask;
item.ProtoExtra = profileItem.ProtoExtra; item.ProtoExtra = profileItem.ProtoExtra;
} }
@ -1122,6 +1123,7 @@ public static class ConfigHandler
&& AreEqual(o.Fingerprint, n.Fingerprint) && AreEqual(o.Fingerprint, n.Fingerprint)
&& AreEqual(o.PublicKey, n.PublicKey) && AreEqual(o.PublicKey, n.PublicKey)
&& AreEqual(o.ShortId, n.ShortId) && AreEqual(o.ShortId, n.ShortId)
&& AreEqual(o.Finalmask, n.Finalmask)
&& (!remarks || o.Remarks == n.Remarks); && (!remarks || o.Remarks == n.Remarks);
static bool AreEqual(string? a, string? b) static bool AreEqual(string? a, string? b)

View file

@ -73,6 +73,19 @@ public class BaseFmt
{ {
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha)); dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
} }
if (item.Finalmask.IsNotEmpty())
{
var node = JsonUtils.ParseJson(item.Finalmask);
var finalmask = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: item.Finalmask;
dicQuery.Add("fm", Utils.UrlEncode(finalmask));
}
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp)); dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
@ -214,6 +227,24 @@ public class BaseFmt
item.EchConfigList = GetQueryDecoded(query, "ech"); item.EchConfigList = GetQueryDecoded(query, "ech");
item.CertSha = GetQueryDecoded(query, "pcs"); item.CertSha = GetQueryDecoded(query, "pcs");
var finalmaskDecoded = GetQueryDecoded(query, "fm");
if (finalmaskDecoded.IsNotEmpty())
{
var node = JsonUtils.ParseJson(finalmaskDecoded);
item.Finalmask = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: finalmaskDecoded;
}
else
{
item.Finalmask = string.Empty;
}
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1")) if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
{ {
item.AllowInsecure = Global.AllowInsecure.First(); item.AllowInsecure = Global.AllowInsecure.First();

View file

@ -178,6 +178,7 @@ public class ProfileItem
public string CertSha { get; set; } public string CertSha { get; set; }
public string EchConfigList { get; set; } public string EchConfigList { get; set; }
public string EchForceQuery { get; set; } public string EchForceQuery { get; set; }
public string Finalmask { get; set; }
public string ProtoExtra { get; set; } public string ProtoExtra { get; set; }

View file

@ -341,7 +341,7 @@ public class StreamSettings4Ray
public HysteriaSettings4Ray? hysteriaSettings { get; set; } public HysteriaSettings4Ray? hysteriaSettings { get; set; }
public FinalMask4Ray? finalmask { get; set; } public Finalmask4Ray? finalmask { get; set; }
public Sockopt4Ray? sockopt { get; set; } public Sockopt4Ray? sockopt { get; set; }
} }
@ -476,7 +476,7 @@ public class HysteriaUdpHop4Ray
public string? interval { get; set; } public string? interval { get; set; }
} }
public class FinalMask4Ray public class Finalmask4Ray
{ {
public List<Mask4Ray>? tcp { get; set; } public List<Mask4Ray>? tcp { get; set; }
public List<Mask4Ray>? udp { get; set; } public List<Mask4Ray>? udp { get; set; }
@ -485,7 +485,7 @@ public class FinalMask4Ray
public class Mask4Ray public class Mask4Ray
{ {
public string type { get; set; } public string type { get; set; }
public MaskSettings4Ray? settings { get; set; } public object? settings { get; set; }
} }
public class MaskSettings4Ray public class MaskSettings4Ray

View file

@ -2916,6 +2916,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Finalmask 的本地化字符串。
/// </summary>
public static string TbFinalmask {
get {
return ResourceManager.GetString("TbFinalmask", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Fingerprint 的本地化字符串。 /// 查找类似 Fingerprint 的本地化字符串。
/// </summary> /// </summary>

View file

@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuServerListPreview" xml:space="preserve"> <data name="menuServerListPreview" xml:space="preserve">
<value>Configuration item preview</value> <value>Configuration item preview</value>
</data> </data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
</root> </root>

View file

@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuServerListPreview" xml:space="preserve"> <data name="menuServerListPreview" xml:space="preserve">
<value>Configuration item preview</value> <value>Configuration item preview</value>
</data> </data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
</root> </root>

View file

@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuServerListPreview" xml:space="preserve"> <data name="menuServerListPreview" xml:space="preserve">
<value>Configuration item preview</value> <value>Configuration item preview</value>
</data> </data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
</root> </root>

View file

@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuServerListPreview" xml:space="preserve"> <data name="menuServerListPreview" xml:space="preserve">
<value>Configuration item preview</value> <value>Configuration item preview</value>
</data> </data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
</root> </root>

View file

@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuServerListPreview" xml:space="preserve"> <data name="menuServerListPreview" xml:space="preserve">
<value>Configuration item preview</value> <value>Configuration item preview</value>
</data> </data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
</root> </root>

View file

@ -1668,4 +1668,7 @@
<data name="menuServerListPreview" xml:space="preserve"> <data name="menuServerListPreview" xml:space="preserve">
<value>子配置项预览</value> <value>子配置项预览</value>
</data> </data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
</root> </root>

View file

@ -1668,4 +1668,7 @@
<data name="menuServerListPreview" xml:space="preserve"> <data name="menuServerListPreview" xml:space="preserve">
<value>Configuration item preview</value> <value>Configuration item preview</value>
</data> </data>
<data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value>
</data>
</root> </root>

View file

@ -275,14 +275,7 @@ public partial class CoreConfigV2rayService
var useragent = ""; var useragent = "";
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty()) if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
{ {
try useragent = Global.UserAgentTexts.GetValueOrDefault(_config.CoreBasicItem.DefUserAgent, _config.CoreBasicItem.DefUserAgent);
{
useragent = Global.UserAgentTexts[_config.CoreBasicItem.DefUserAgent];
}
catch (KeyNotFoundException)
{
useragent = _config.CoreBasicItem.DefUserAgent;
}
} }
//if tls //if tls
@ -587,6 +580,11 @@ public partial class CoreConfigV2rayService
} }
break; break;
} }
if (!node.Finalmask.IsNullOrEmpty())
{
streamSettings.finalmask = JsonUtils.Deserialize<Finalmask4Ray>(node.Finalmask);
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View file

@ -32,7 +32,7 @@
IsCancel="True" /> IsCancel="True" />
</StackPanel> </StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> <Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Grid <Grid
Grid.Row="0" Grid.Row="0"
@ -407,7 +407,7 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPorts7Tips}" /> Text="{x:Static resx:ResUI.TbPorts7Tips}" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
@ -420,7 +420,7 @@
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="5" Grid.Row="5"
Grid.Column="0" Grid.Column="0"
@ -997,6 +997,27 @@
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
</Grid> </Grid>
<Separator Grid.Row="8" Margin="{StaticResource MarginTb8}" /> <Separator Grid.Row="8" Margin="{StaticResource MarginTb8}" />
<Grid
x:Name="gridFinalmask"
Grid.Row="9"
ColumnDefinitions="300,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbFinalmask}" />
<TextBox
x:Name="txtFinalmask"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
AcceptsReturn="True"
Classes="TextArea"
TextWrapping="NoWrap" />
</Grid>
<Separator Grid.Row="10" Margin="{StaticResource MarginTb8}" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>

View file

@ -78,6 +78,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
cmbCoreType.IsEnabled = false; cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false; cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty; cmbFingerprint.SelectedValue = string.Empty;
gridFinalmask.IsVisible = false;
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
break; break;
@ -95,6 +96,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
gridAnytls.IsVisible = true; gridAnytls.IsVisible = true;
lstStreamSecurity.Add(Global.StreamSecurityReality); lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbCoreType.IsEnabled = false; cmbCoreType.IsEnabled = false;
gridFinalmask.IsVisible = false;
break; break;
} }
cmbStreamSecurity.ItemsSource = lstStreamSecurity; cmbStreamSecurity.ItemsSource = lstStreamSecurity;
@ -194,6 +196,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);

View file

@ -60,6 +60,8 @@
<RowDefinition Height="Auto" /> <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 Grid.Row="0"> <Grid Grid.Row="0">
@ -1267,6 +1269,36 @@
Grid.Row="8" Grid.Row="8"
Margin="0,2" Margin="0,2"
Style="{DynamicResource MaterialDesignSeparator}" /> Style="{DynamicResource MaterialDesignSeparator}" />
<Grid x:Name="gridFinalmask" Grid.Row="9">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbFinalmask}" />
<TextBox
x:Name="txtFinalmask"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
AcceptsReturn="True"
HorizontalScrollBarVisibility="Auto"
MaxLines="8"
MinLines="4"
Style="{StaticResource MyOutlinedTextBox}"
TextWrapping="NoWrap"
VerticalScrollBarVisibility="Auto" />
</Grid>
<Separator
Grid.Row="10"
Margin="0,2"
Style="{DynamicResource MaterialDesignSeparator}" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>

View file

@ -73,6 +73,7 @@ public partial class AddServerWindow
cmbCoreType.IsEnabled = false; cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false; cmbFingerprint.IsEnabled = false;
cmbFingerprint.Text = string.Empty; cmbFingerprint.Text = string.Empty;
gridFinalmask.Visibility = Visibility.Collapsed;
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
break; break;
@ -90,6 +91,7 @@ public partial class AddServerWindow
gridAnytls.Visibility = Visibility.Visible; gridAnytls.Visibility = Visibility.Visible;
cmbCoreType.IsEnabled = false; cmbCoreType.IsEnabled = false;
lstStreamSecurity.Add(Global.StreamSecurityReality); lstStreamSecurity.Add(Global.StreamSecurityReality);
gridFinalmask.Visibility = Visibility.Collapsed;
break; break;
} }
cmbStreamSecurity.ItemsSource = lstStreamSecurity; cmbStreamSecurity.ItemsSource = lstStreamSecurity;
@ -190,6 +192,8 @@ public partial class AddServerWindow
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);