From 78615cb3d1b096eb2762bcd1a85ddf77032bb27f Mon Sep 17 00:00:00 2001
From: WangJie <569937993@qq.com>
Date: Sun, 18 Jan 2026 19:13:37 +0800
Subject: [PATCH] feat: Add custom FakeIP configuration to DNS settings with
dedicated UI, validation, and localization.
---
v2rayN/ServiceLib/Models/ConfigItems.cs | 2 +
v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 12 ++++
v2rayN/ServiceLib/Resx/ResUI.fr.resx | 12 ++++
v2rayN/ServiceLib/Resx/ResUI.hu.resx | 12 ++++
v2rayN/ServiceLib/Resx/ResUI.resx | 12 ++++
v2rayN/ServiceLib/Resx/ResUI.ru.resx | 12 ++++
v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 12 ++++
v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 12 ++++
.../CoreConfig/Singbox/SingboxDnsService.cs | 12 +++-
.../ViewModels/DNSSettingViewModel.cs | 60 +++++++++++++++++++
.../Views/DNSSettingWindow.axaml | 47 +++++++++++++++
.../Views/DNSSettingWindow.axaml.cs | 5 ++
12 files changed, 209 insertions(+), 1 deletion(-)
diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs
index eeb88deb..615529ac 100644
--- a/v2rayN/ServiceLib/Models/ConfigItems.cs
+++ b/v2rayN/ServiceLib/Models/ConfigItems.cs
@@ -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; }
}
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index 3a720cdd..4a910de7 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -1014,6 +1014,18 @@
sing-box Custom DNS
+
+ Custom FakeIP
+
+
+ Enable Custom FakeIP
+
+
+ Import Default FakeIP Config
+
+
+ Validate Format
+
لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
index f1710de6..f7ac3151 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
@@ -1011,6 +1011,18 @@
DNS personnalisé sing-box
+
+ FakeIP personnalisé
+
+
+ Activer FakeIP personnalisé
+
+
+ Importer la config FakeIP par défaut
+
+
+ Valider le format
+
Saisissez la structure JSON DNS ; cliquez pour voir la doc.
diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
index 4787f0f1..7dfd5ec0 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
@@ -1014,6 +1014,18 @@
sing-box Custom DNS
+
+ Custom FakeIP
+
+
+ Enable Custom FakeIP
+
+
+ Import Default FakeIP Config
+
+
+ Validate Format
+
Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index 6ad8a6e4..5324c0de 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1014,6 +1014,18 @@
sing-box Custom DNS
+
+ Custom FakeIP
+
+
+ Enable Custom FakeIP
+
+
+ Import Default FakeIP Config
+
+
+ Validate Format
+
Please fill in DNS Structure, Click to view the document
diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
index 3a0f9fef..514fac6d 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
@@ -1014,6 +1014,18 @@
sing-box Custom DNS
+
+ Custom FakeIP
+
+
+ Enable Custom FakeIP
+
+
+ Import Default FakeIP Config
+
+
+ Validate Format
+
Заполните структуру DNS, нажмите, чтобы открыть документ
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index b37e0ccb..6b1d95a1 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1011,6 +1011,18 @@
sing-box 自定义 DNS
+
+ 自定义 FakeIP
+
+
+ 启用自定义 FakeIP
+
+
+ 点击导入默认 FakeIP 设置
+
+
+ 验证格式
+
请填写 DNS JSON 结构,点击查看文档
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
index b5acd049..8f8d549e 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
@@ -1011,6 +1011,18 @@
sing-box 自訂 DNS
+
+ 自訂 FakeIP
+
+
+ 啟用自訂 FakeIP
+
+
+ 點擊匯入預設 FakeIP 設定
+
+
+ 驗證格式
+
請填寫 DNS JSON 結構,點擊查看檔案
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
index fa089140..57909d66 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
@@ -228,7 +228,17 @@ public partial class CoreConfigSingboxService
if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == true)
{
- var fakeipFilterRule = JsonUtils.Deserialize(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
+ var strFakeipFilter = EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName);
+ if (simpleDNSItem.EnableCustomFakeIP == true && simpleDNSItem.CustomFakeIPFilter.IsNotEmpty())
+ {
+ strFakeipFilter = simpleDNSItem.CustomFakeIPFilter;
+ }
+
+ var fakeipFilterRule = JsonUtils.Deserialize(strFakeipFilter);
+ if (fakeipFilterRule == null)
+ {
+ fakeipFilterRule = JsonUtils.Deserialize(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
+ }
fakeipFilterRule.invert = true;
var rule4Fake = new Rule4Sbox
{
diff --git a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs
index d53b960d..555c23da 100644
--- a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs
@@ -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 SaveCmd { get; }
public ReactiveCommand ImportDefConfig4V2rayCompatibleCmd { get; }
public ReactiveCommand ImportDefConfig4SingboxCompatibleCmd { get; }
+ public ReactiveCommand ImportDefFakeIPConfigCmd { get; }
+ public ReactiveCommand ValidateFakeIPConfigCmd { get; }
public DNSSettingViewModel(Func>? 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(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(CustomFakeIPFilter);
+ if (obj == null)
+ {
+ NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
+ return;
+ }
+ }
+ catch
+ {
+ NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
+ return;
+ }
+ }
if (NormalDNSCompatible.IsNotEmpty())
{
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
index e5a785b7..7159e50c 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml
@@ -276,6 +276,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
index e673a025..3bb7ec16 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
@@ -61,6 +61,11 @@ public partial class DNSSettingWindow : WindowBase
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("txtBasicDNSSettingsInvalid"), t => t.IsVisible);