feat: Add custom FakeIP configuration to DNS settings with dedicated UI, validation, and localization.

This commit is contained in:
WangJie 2026-01-18 19:13:37 +08:00
parent 03b62b3d78
commit 78615cb3d1
12 changed files with 209 additions and 1 deletions

View file

@ -270,4 +270,6 @@ public class SimpleDNSItem
public string? SingboxStrategy4Proxy { get; set; }
public string? Hosts { get; set; }
public string? DirectExpectedIPs { get; set; }
public bool? EnableCustomFakeIP { get; set; }
public string? CustomFakeIPFilter { get; set; }
}

View file

@ -1014,6 +1014,18 @@
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box Custom DNS</value>
</data>
<data name="TbCustomFakeIP" xml:space="preserve">
<value>Custom FakeIP</value>
</data>
<data name="TbEnableCustomFakeIP" xml:space="preserve">
<value>Enable Custom FakeIP</value>
</data>
<data name="TbImportDefFakeIPConfig" xml:space="preserve">
<value>Import Default FakeIP Config</value>
</data>
<data name="TbValidateFakeIPConfig" xml:space="preserve">
<value>Validate Format</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
</data>

View file

@ -1011,6 +1011,18 @@
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>DNS personnalisé sing-box</value>
</data>
<data name="TbCustomFakeIP" xml:space="preserve">
<value>FakeIP personnalisé</value>
</data>
<data name="TbEnableCustomFakeIP" xml:space="preserve">
<value>Activer FakeIP personnalisé</value>
</data>
<data name="TbImportDefFakeIPConfig" xml:space="preserve">
<value>Importer la config FakeIP par défaut</value>
</data>
<data name="TbValidateFakeIPConfig" xml:space="preserve">
<value>Valider le format</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Saisissez la structure JSON DNS ; cliquez pour voir la doc.</value>
</data>

View file

@ -1014,6 +1014,18 @@
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box Custom DNS</value>
</data>
<data name="TbCustomFakeIP" xml:space="preserve">
<value>Custom FakeIP</value>
</data>
<data name="TbEnableCustomFakeIP" xml:space="preserve">
<value>Enable Custom FakeIP</value>
</data>
<data name="TbImportDefFakeIPConfig" xml:space="preserve">
<value>Import Default FakeIP Config</value>
</data>
<data name="TbValidateFakeIPConfig" xml:space="preserve">
<value>Validate Format</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez</value>
</data>

View file

@ -1014,6 +1014,18 @@
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box Custom DNS</value>
</data>
<data name="TbCustomFakeIP" xml:space="preserve">
<value>Custom FakeIP</value>
</data>
<data name="TbEnableCustomFakeIP" xml:space="preserve">
<value>Enable Custom FakeIP</value>
</data>
<data name="TbImportDefFakeIPConfig" xml:space="preserve">
<value>Import Default FakeIP Config</value>
</data>
<data name="TbValidateFakeIPConfig" xml:space="preserve">
<value>Validate Format</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Please fill in DNS Structure, Click to view the document</value>
</data>

View file

@ -1014,6 +1014,18 @@
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box Custom DNS</value>
</data>
<data name="TbCustomFakeIP" xml:space="preserve">
<value>Custom FakeIP</value>
</data>
<data name="TbEnableCustomFakeIP" xml:space="preserve">
<value>Enable Custom FakeIP</value>
</data>
<data name="TbImportDefFakeIPConfig" xml:space="preserve">
<value>Import Default FakeIP Config</value>
</data>
<data name="TbValidateFakeIPConfig" xml:space="preserve">
<value>Validate Format</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
</data>

View file

@ -1011,6 +1011,18 @@
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box 自定义 DNS</value>
</data>
<data name="TbCustomFakeIP" xml:space="preserve">
<value>自定义 FakeIP</value>
</data>
<data name="TbEnableCustomFakeIP" xml:space="preserve">
<value>启用自定义 FakeIP</value>
</data>
<data name="TbImportDefFakeIPConfig" xml:space="preserve">
<value>点击导入默认 FakeIP 设置</value>
</data>
<data name="TbValidateFakeIPConfig" xml:space="preserve">
<value>验证格式</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>请填写 DNS JSON 结构,点击查看文档</value>
</data>

View file

@ -1011,6 +1011,18 @@
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box 自訂 DNS</value>
</data>
<data name="TbCustomFakeIP" xml:space="preserve">
<value>自訂 FakeIP</value>
</data>
<data name="TbEnableCustomFakeIP" xml:space="preserve">
<value>啟用自訂 FakeIP</value>
</data>
<data name="TbImportDefFakeIPConfig" xml:space="preserve">
<value>點擊匯入預設 FakeIP 設定</value>
</data>
<data name="TbValidateFakeIPConfig" xml:space="preserve">
<value>驗證格式</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>請填寫 DNS JSON 結構,點擊查看檔案</value>
</data>

View file

@ -228,7 +228,17 @@ public partial class CoreConfigSingboxService
if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == true)
{
var fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
var strFakeipFilter = EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName);
if (simpleDNSItem.EnableCustomFakeIP == true && simpleDNSItem.CustomFakeIPFilter.IsNotEmpty())
{
strFakeipFilter = simpleDNSItem.CustomFakeIPFilter;
}
var fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(strFakeipFilter);
if (fakeipFilterRule == null)
{
fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
}
fakeipFilterRule.invert = true;
var rule4Fake = new Rule4Sbox
{

View file

@ -14,6 +14,8 @@ public class DNSSettingViewModel : MyReactiveObject
[Reactive] public string? SingboxStrategy4Proxy { get; set; }
[Reactive] public string? Hosts { get; set; }
[Reactive] public string? DirectExpectedIPs { get; set; }
[Reactive] public bool? EnableCustomFakeIP { get; set; }
[Reactive] public string? CustomFakeIPFilter { get; set; }
[Reactive] public bool UseSystemHostsCompatible { get; set; }
[Reactive] public string DomainStrategy4FreedomCompatible { get; set; }
@ -32,6 +34,8 @@ public class DNSSettingViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCompatibleCmd { get; }
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCompatibleCmd { get; }
public ReactiveCommand<Unit, Unit> ImportDefFakeIPConfigCmd { get; }
public ReactiveCommand<Unit, Unit> ValidateFakeIPConfigCmd { get; }
public DNSSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
@ -52,6 +56,39 @@ public class DNSSettingViewModel : MyReactiveObject
await Task.CompletedTask;
});
ImportDefFakeIPConfigCmd = ReactiveCommand.CreateFromTask(async () =>
{
CustomFakeIPFilter = EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName);
await Task.CompletedTask;
});
ValidateFakeIPConfigCmd = ReactiveCommand.CreateFromTask(async () =>
{
if (CustomFakeIPFilter.IsNullOrEmpty())
{
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
await Task.CompletedTask;
return;
}
try
{
var obj = JsonUtils.Deserialize<Rule4Sbox>(CustomFakeIPFilter);
if (obj == null)
{
NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
}
else
{
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
}
}
catch
{
NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
}
await Task.CompletedTask;
});
this.WhenAnyValue(x => x.RayCustomDNSEnableCompatible, x => x.SBCustomDNSEnableCompatible)
.Select(x => !(x.Item1 && x.Item2))
.ToPropertyEx(this, x => x.IsSimpleDNSEnabled);
@ -75,6 +112,8 @@ public class DNSSettingViewModel : MyReactiveObject
SingboxStrategy4Proxy = item.SingboxStrategy4Proxy;
Hosts = item.Hosts;
DirectExpectedIPs = item.DirectExpectedIPs;
EnableCustomFakeIP = item.EnableCustomFakeIP;
CustomFakeIPFilter = item.CustomFakeIPFilter;
var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
RayCustomDNSEnableCompatible = item1.Enabled;
@ -105,6 +144,27 @@ public class DNSSettingViewModel : MyReactiveObject
_config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy;
_config.SimpleDNSItem.Hosts = Hosts;
_config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs;
_config.SimpleDNSItem.EnableCustomFakeIP = EnableCustomFakeIP;
_config.SimpleDNSItem.CustomFakeIPFilter = CustomFakeIPFilter;
// Validate custom FakeIP filter JSON format
if (EnableCustomFakeIP == true && CustomFakeIPFilter.IsNotEmpty())
{
try
{
var obj = JsonUtils.Deserialize<Rule4Sbox>(CustomFakeIPFilter);
if (obj == null)
{
NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
return;
}
}
catch
{
NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
return;
}
}
if (NormalDNSCompatible.IsNotEmpty())
{

View file

@ -276,6 +276,53 @@
</ScrollViewer>
</TabItem>
<TabItem Header="{x:Static resx:ResUI.TbCustomFakeIP}">
<DockPanel Margin="{StaticResource Margin8}">
<Grid DockPanel.Dock="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbEnableCustomFakeIP}" />
<ToggleSwitch
x:Name="togEnableCustomFakeIP"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" IsEnabled="{Binding ElementName=togEnableCustomFakeIP, Path=IsChecked}">
<Button
x:Name="btnImportDefFakeIPConfig"
Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbImportDefFakeIPConfig}" />
<Button
x:Name="btnValidateFakeIPConfig"
Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbValidateFakeIPConfig}" />
</StackPanel>
</Grid>
<HeaderedContentControl
Margin="{StaticResource Margin4}"
BorderBrush="Gray"
BorderThickness="1"
Header="FakeIP Filter (JSON)">
<TextBox
Name="txtCustomFakeIPFilter"
VerticalAlignment="Stretch"
Classes="TextArea"
MinLines="10"
TextWrapping="Wrap"
IsEnabled="{Binding ElementName=togEnableCustomFakeIP, Path=IsChecked}" />
</HeaderedContentControl>
</DockPanel>
</TabItem>
<TabItem Header="{x:Static resx:ResUI.TbCustomDnsRay}">
<DockPanel Margin="{StaticResource Margin8}">
<Grid DockPanel.Dock="Top">

View file

@ -61,6 +61,11 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableCustomFakeIP, v => v.togEnableCustomFakeIP.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CustomFakeIPFilter, v => v.txtCustomFakeIPFilter.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefFakeIPConfigCmd, v => v.btnImportDefFakeIPConfig).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ValidateFakeIPConfigCmd, v => v.btnValidateFakeIPConfig).DisposeWith(disposables);
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
.Select(b => !b)
.BindTo(this.FindControl<TextBlock>("txtBasicDNSSettingsInvalid"), t => t.IsVisible);