diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 37176608..96c1ca9b 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -254,6 +254,7 @@ public static class ConfigHandler item.CertSha = profileItem.CertSha; item.EchConfigList = profileItem.EchConfigList; item.EchForceQuery = profileItem.EchForceQuery; + item.Finalmask = profileItem.Finalmask; item.ProtoExtra = profileItem.ProtoExtra; } @@ -1122,6 +1123,7 @@ public static class ConfigHandler && AreEqual(o.Fingerprint, n.Fingerprint) && AreEqual(o.PublicKey, n.PublicKey) && AreEqual(o.ShortId, n.ShortId) + && AreEqual(o.Finalmask, n.Finalmask) && (!remarks || o.Remarks == n.Remarks); static bool AreEqual(string? a, string? b) diff --git a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index bfafce13..b620cfe9 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -73,6 +73,19 @@ public class BaseFmt { 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)); @@ -214,6 +227,24 @@ public class BaseFmt item.EchConfigList = GetQueryDecoded(query, "ech"); 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")) { item.AllowInsecure = Global.AllowInsecure.First(); diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index 6e9d2a30..50ac8b90 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -178,6 +178,7 @@ public class ProfileItem public string CertSha { get; set; } public string EchConfigList { get; set; } public string EchForceQuery { get; set; } + public string Finalmask { get; set; } public string ProtoExtra { get; set; } diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index 34456a8e..53d34563 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -341,7 +341,7 @@ public class StreamSettings4Ray public HysteriaSettings4Ray? hysteriaSettings { get; set; } - public FinalMask4Ray? finalmask { get; set; } + public Finalmask4Ray? finalmask { get; set; } public Sockopt4Ray? sockopt { get; set; } } @@ -476,7 +476,7 @@ public class HysteriaUdpHop4Ray public string? interval { get; set; } } -public class FinalMask4Ray +public class Finalmask4Ray { public List? tcp { get; set; } public List? udp { get; set; } @@ -485,7 +485,7 @@ public class FinalMask4Ray public class Mask4Ray { public string type { get; set; } - public MaskSettings4Ray? settings { get; set; } + public object? settings { get; set; } } public class MaskSettings4Ray diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 6f223693..20637ed5 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2916,6 +2916,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Finalmask 的本地化字符串。 + /// + public static string TbFinalmask { + get { + return ResourceManager.GetString("TbFinalmask", resourceCulture); + } + } + /// /// 查找类似 Fingerprint 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index dbce6ac4..c93a9292 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Configuration item preview + + Finalmask + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index 005a51d5..b2e636a9 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Configuration item preview + + Finalmask + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 3d169590..9749f552 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Configuration item preview + + Finalmask + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 041a103b..1ff10bec 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Configuration item preview + + Finalmask + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 66ebef79..f65a967d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1671,4 +1671,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Configuration item preview + + Finalmask + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index c8936668..e185636c 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1668,4 +1668,7 @@ 子配置项预览 + + Finalmask + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 2ee4dc4f..8b2c4cff 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1668,4 +1668,7 @@ Configuration item preview + + Finalmask + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 7f9c26ca..10f3e592 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -275,14 +275,7 @@ public partial class CoreConfigV2rayService var useragent = ""; if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty()) { - try - { - useragent = Global.UserAgentTexts[_config.CoreBasicItem.DefUserAgent]; - } - catch (KeyNotFoundException) - { - useragent = _config.CoreBasicItem.DefUserAgent; - } + useragent = Global.UserAgentTexts.GetValueOrDefault(_config.CoreBasicItem.DefUserAgent, _config.CoreBasicItem.DefUserAgent); } //if tls @@ -587,6 +580,11 @@ public partial class CoreConfigV2rayService } break; } + + if (!node.Finalmask.IsNullOrEmpty()) + { + streamSettings.finalmask = JsonUtils.Deserialize(node.Finalmask); + } } catch (Exception ex) { diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index 356698fb..28d82319 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -32,7 +32,7 @@ IsCancel="True" /> - + - + - + + + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 250bbe78..fdd097b2 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -78,6 +78,7 @@ public partial class AddServerWindow : WindowBase cmbCoreType.IsEnabled = false; cmbFingerprint.IsEnabled = false; cmbFingerprint.SelectedValue = string.Empty; + gridFinalmask.IsVisible = false; cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; break; @@ -95,6 +96,7 @@ public partial class AddServerWindow : WindowBase gridAnytls.IsVisible = true; lstStreamSecurity.Add(Global.StreamSecurityReality); cmbCoreType.IsEnabled = false; + gridFinalmask.IsVisible = false; break; } cmbStreamSecurity.ItemsSource = lstStreamSecurity; @@ -194,6 +196,8 @@ public partial class AddServerWindow : WindowBase 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.Finalmask, v => v.txtFinalmask.Text).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.SaveCmd, v => v.btnSave).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index 8c6b4a35..87d53a40 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -60,6 +60,8 @@ + + @@ -1267,6 +1269,36 @@ Grid.Row="8" Margin="0,2" Style="{DynamicResource MaterialDesignSeparator}" /> + + + + + + + + + diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 86d9cac1..4875afa5 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -73,6 +73,7 @@ public partial class AddServerWindow cmbCoreType.IsEnabled = false; cmbFingerprint.IsEnabled = false; cmbFingerprint.Text = string.Empty; + gridFinalmask.Visibility = Visibility.Collapsed; cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; break; @@ -90,6 +91,7 @@ public partial class AddServerWindow gridAnytls.Visibility = Visibility.Visible; cmbCoreType.IsEnabled = false; lstStreamSecurity.Add(Global.StreamSecurityReality); + gridFinalmask.Visibility = Visibility.Collapsed; break; } 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.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.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);