feat(dns): support optional IPv6 FakeIP pools (#10464)

This commit is contained in:
yc99-gif
2026-11-04 09:51:43 +08:00
committed by GitHub
parent 0d9e84556e
commit 2239982a63
19 changed files with 420 additions and 28 deletions
@@ -0,0 +1,185 @@
namespace ServiceLib.Tests.CoreConfig;
public class DualStackFakeIPTests
{
[Test]
[Arguments(null, "fc00::/18", 65535L)]
[Arguments(" 2001:2::/48 ", "2001:2::/48", 65535L)]
[Arguments("fc00::/120", "fc00::/120", 255L)]
[Arguments("fc00::/127", "fc00::/127", 1L)]
public async Task IPv6Range_ShouldUseAnXrayCompatiblePoolSize(string? input, string expectedRange, long expectedSize)
{
var valid = FakeIPUtils.TryParseIPv6Range(input, out var range, out var size);
await valid.Should().BeTrue();
await range.Should().BeEqualTo(expectedRange);
await size.Should().BeEqualTo(expectedSize);
}
[Test]
[Arguments("198.18.0.0/15")]
[Arguments("not-a-network")]
[Arguments("fc00::/128")]
public async Task IPv6Range_ShouldRejectInvalidOrSingleAddressPools(string input)
{
await FakeIPUtils.TryParseIPv6Range(input, out _, out _).Should().BeFalse();
}
[Test]
public async Task OldDnsSettings_ShouldKeepIPv6FakeIPDisabled()
{
var settings = JsonUtils.Deserialize<SimpleDNSItem>("""
{ "FakeIP": true, "FakeIPRange": "198.18.0.0/15" }
""")!;
await (settings.FakeIPv6 == true).Should().BeFalse();
await settings.FakeIPRange.Should().BeEqualTo("198.18.0.0/15");
}
[Test]
public async Task DnsSettings_ShouldRoundTripBothAddressRanges()
{
var settings = new SimpleDNSItem
{
FakeIP = true,
FakeIPRange = "198.18.0.0/15",
FakeIPv6 = true,
FakeIPv6Range = "2001:2::/48",
};
var restored = JsonUtils.Deserialize<SimpleDNSItem>(JsonUtils.Serialize(settings))!;
await restored.FakeIP.Should().BeEqualTo(true);
await restored.FakeIPRange.Should().BeEqualTo(settings.FakeIPRange);
await restored.FakeIPv6.Should().BeEqualTo(true);
await restored.FakeIPv6Range.Should().BeEqualTo(settings.FakeIPv6Range);
}
[Test]
public async Task AppConfig_ShouldRoundTripIPv6Settings()
{
var config = CoreConfigTestFactory.CreateConfig();
config.SimpleDNSItem.FakeIPv6 = true;
config.SimpleDNSItem.FakeIPv6Range = "2001:2::/48";
var restored = JsonUtils.Deserialize<Config>(JsonUtils.Serialize(config))!;
await restored.Should().NotBeNull();
await restored.SimpleDNSItem.FakeIPv6.Should().BeEqualTo(true);
await restored.SimpleDNSItem.FakeIPv6Range.Should().BeEqualTo("2001:2::/48");
}
[Test]
[Arguments(false)]
[Arguments(true)]
public async Task Xray_ShouldRoundTripSingleAndDualPools(bool enableIPv6)
{
var context = CreateContext(ECoreType.Xray, enableIPv6, false);
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue();
var restored = JsonUtils.Deserialize<V2rayConfig>(result.Data!.ToString())!;
await restored.Should().NotBeNull();
var roundTrip = JsonUtils.ParseJson(JsonUtils.Serialize(restored))!;
var original = JsonUtils.ParseJson(result.Data!.ToString())!;
await JsonNode.DeepEquals(original["fakedns"], roundTrip["fakedns"]).Should().BeTrue();
await (restored.fakedns is JsonArray).Should().BeEqualTo(enableIPv6);
await restored.fakedns.Should().NotBeNull();
}
[Test]
[Arguments(false, false)]
[Arguments(true, false)]
[Arguments(true, true)]
public async Task Xray_ShouldAddIPv6OnlyWhenEnabledAndAAAAAllowed(bool enableIPv6, bool blockAAAA)
{
var context = CreateContext(ECoreType.Xray, enableIPv6, blockAAAA);
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue();
var root = JsonUtils.ParseJson(result.Data!.ToString())!;
var dualStack = enableIPv6 && !blockAAAA;
await (root["fakedns"] is JsonArray).Should().BeEqualTo(dualStack);
var pool = dualStack ? root["fakedns"]![0]! : root["fakedns"]!;
await pool["ipPool"]!.GetValue<string>().Should().BeEqualTo("198.18.0.0/15");
if (dualStack)
{
await root["fakedns"]!.AsArray().Count.Should().BeEqualTo(2);
await root["fakedns"]![1]!["ipPool"]!.GetValue<string>().Should().BeEqualTo("2001:2::/48");
await root["fakedns"]![1]!["poolSize"]!.GetValue<long>().Should().BeEqualTo(65535L);
}
if (blockAAAA)
{
await root["dns"]!["queryStrategy"]!.GetValue<string>().Should().BeEqualTo("UseIPv4");
}
}
[Test]
[Arguments(false, false)]
[Arguments(true, false)]
[Arguments(true, true)]
public async Task Singbox_ShouldAddIPv6OnlyWhenEnabledAndAAAAAllowed(bool enableIPv6, bool blockAAAA)
{
var context = CreateContext(ECoreType.sing_box, enableIPv6, blockAAAA);
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue();
var config = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
var fakeDns = config.dns.servers.Single(server => server.type == "fakeip");
await fakeDns.inet4_range.Should().BeEqualTo("198.18.0.0/15");
await fakeDns.inet6_range.Should().BeEqualTo(enableIPv6 && !blockAAAA ? "2001:2::/48" : null);
}
[Test]
public async Task DisabledFakeIP_ShouldNotCreateEitherPool()
{
foreach (var coreType in new[] { ECoreType.Xray, ECoreType.sing_box })
{
var context = CreateContext(coreType, true, false);
context.AppConfig.SimpleDNSItem.FakeIP = false;
var result = coreType == ECoreType.Xray
? new CoreConfigV2rayService(context).GenerateClientConfigContent()
: new CoreConfigSingboxService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue();
var root = JsonUtils.ParseJson(result.Data!.ToString())!;
await root["fakedns"].Should().BeNull();
await root["dns"]!["servers"]!.AsArray()
.Any(server => server is JsonObject obj && obj["type"]?.GetValue<string>() == "fakeip")
.Should().BeFalse();
}
}
private static CoreConfigContext CreateContext(ECoreType coreType, bool enableIPv6, bool blockAAAA)
{
var config = CoreConfigTestFactory.CreateConfig(coreType);
config.SimpleDNSItem.FakeIP = true;
config.SimpleDNSItem.FakeIPRange = "198.18.0.0/15";
config.SimpleDNSItem.FakeIPv6 = enableIPv6;
config.SimpleDNSItem.FakeIPv6Range = "2001:2::/48";
config.SimpleDNSItem.BlockAAAAQuery = blockAAAA;
CoreConfigTestFactory.BindAppManagerConfig(config);
var node = CoreConfigTestFactory.CreateVmessNode(coreType);
return CoreConfigTestFactory.CreateContext(config, node, coreType) with
{
RoutingItem = new RoutingItem
{
Id = "fakeip-test",
Remarks = "FakeIP test",
DomainStrategy = Global.AsIs,
DomainStrategy4Singbox = string.Empty,
RuleSet = JsonUtils.Serialize(new List<RulesItem>
{
new()
{
Enabled = true,
RuleType = ERuleType.DNS,
OutboundTag = Global.ProxyTag,
Domain = ["full:example.com"],
},
}),
},
};
}
}
@@ -109,8 +109,8 @@ public class CoreConfigV2rayIPNetworkTests
var generated = JsonUtils.Deserialize<V2rayConfig>(result.Data!.ToString())!;
await generated.fakedns.Should().NotBeNull();
await generated.fakedns!.ipPool.Should().BeEqualTo(range);
await generated.fakedns.poolSize.Should().BeEqualTo(expectedPoolSize);
await generated.fakedns!["ipPool"]!.GetValue<string>().Should().BeEqualTo(range);
await generated.fakedns["poolSize"]!.GetValue<long>().Should().BeEqualTo(expectedPoolSize);
}
private static async Task<V2rayConfig> GenerateConfig(Config config)
+25
View File
@@ -0,0 +1,25 @@
namespace ServiceLib.Common;
public static class FakeIPUtils
{
public static bool TryParseIPv6Range(string? value, out string range, out long poolSize)
{
range = string.IsNullOrWhiteSpace(value) ? Global.FakeIPv6Ranges.First() : value.Trim();
poolSize = 65535;
if (!System.Net.IPNetwork.TryParse(range, out var network)
|| network.BaseAddress.AddressFamily != AddressFamily.InterNetworkV6
|| network.PrefixLength == 128)
{
return false;
}
range = network.ToString();
var hostBits = 128 - network.PrefixLength;
if (hostBits < 16)
{
// Xray requires the mapping capacity to be smaller than the address pool.
poolSize = (1L << hostBits) - 1;
}
return true;
}
}
+1
View File
@@ -240,6 +240,7 @@ public class JsonUtils
[JsonSerializable(typeof(List<Rule4Sbox>))]
[JsonSerializable(typeof(List<Ruleset4Sbox>))]
[JsonSerializable(typeof(V2rayConfig))]
[JsonSerializable(typeof(FakeDns4Ray))]
[JsonSerializable(typeof(Dns4Ray))]
[JsonSerializable(typeof(DnsServer4Ray))]
[JsonSerializable(typeof(Inbounds4Ray))]
+6
View File
@@ -773,6 +773,12 @@ public class Global
"11.0.0.0/8",
];
public static readonly List<string> FakeIPv6Ranges =
[
"fc00::/18",
"2001:2::/48",
];
public static readonly List<string> RootCertProviders =
[
"system",
@@ -115,6 +115,8 @@ public static class ConfigHandler
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
config.SimpleDNSItem.BlockAAAAQuery ??= false;
config.SimpleDNSItem.FakeIPRange ??= Global.FakeIPRanges.FirstOrDefault();
config.SimpleDNSItem.FakeIPv6 ??= false;
config.SimpleDNSItem.FakeIPv6Range ??= Global.FakeIPv6Ranges.First();
config.SimpleDNSItem.GlobalFakeIp ??= true;
config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault();
config.SimpleDNSItem.ServeStale ??= false;
@@ -254,6 +254,8 @@ public class SimpleDNSItem
public bool? FakeIP { get; set; }
public bool? GlobalFakeIp { get; set; }
public string? FakeIPRange { get; set; }
public bool? FakeIPv6 { get; set; }
public string? FakeIPv6Range { get; set; }
public bool? BlockBindingQuery { get; set; }
public bool? BlockAAAAQuery { get; set; }
public string? DirectDNS { get; set; }
@@ -4,7 +4,7 @@ public class V2rayConfig
{
public Log4Ray log { get; set; }
public JsonObject? dns { get; set; }
public FakeDns4Ray? fakedns { get; set; }
public JsonNode? fakedns { get; set; }
public List<Inbounds4Ray> inbounds { get; set; }
public List<Outbounds4Ray> outbounds { get; set; }
public Routing4Ray routing { get; set; }
+28 -1
View File
@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
@@ -5444,5 +5444,32 @@ namespace ServiceLib.Resx {
return ResourceManager.GetString("UpgradeAppNotExistTip", resourceCulture);
}
}
/// <summary>
/// IPv6 FakeIP
/// </summary>
public static string TbFakeIPv6 {
get {
return ResourceManager.GetString("TbFakeIPv6", resourceCulture);
}
}
/// <summary>
/// Return fake IPv6 addresses as well. Requires FakeIP to be enabled and AAAA queries to be allowed.
/// </summary>
public static string TbFakeIPv6Tips {
get {
return ResourceManager.GetString("TbFakeIPv6Tips", resourceCulture);
}
}
/// <summary>
/// Enter a valid IPv6 CIDR range with at least two addresses, for example fc00::/18.
/// </summary>
public static string FillCorrectFakeIPv6Range {
get {
return ResourceManager.GetString("FillCorrectFakeIPv6Range", resourceCulture);
}
}
}
}
+10 -1
View File
@@ -1905,6 +1905,15 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuAddMasqueServer" xml:space="preserve">
<value>Add [MASQUE]</value>
</data>
<data name="TbFakeIPv6" xml:space="preserve">
<value>IPv6 FakeIP</value>
</data>
<data name="TbFakeIPv6Tips" xml:space="preserve">
<value>Return fake IPv6 addresses as well. Requires FakeIP to be enabled and AAAA queries to be allowed.</value>
</data>
<data name="FillCorrectFakeIPv6Range" xml:space="preserve">
<value>Enter a valid IPv6 CIDR range with at least two addresses, for example fc00::/18.</value>
</data>
<data name="menuColumns" xml:space="preserve">
<value>Columns</value>
</data>
@@ -1914,4 +1923,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="LvPreResolve" xml:space="preserve">
<value>Pre-resolve server domains</value>
</data>
</root>
</root>
+10 -1
View File
@@ -1897,6 +1897,15 @@
<data name="menuAddMasqueServer" xml:space="preserve">
<value>添加 [MASQUE]</value>
</data>
<data name="TbFakeIPv6" xml:space="preserve">
<value>IPv6 FakeIP</value>
</data>
<data name="TbFakeIPv6Tips" xml:space="preserve">
<value>同时返回 IPv6 假地址,需启用 FakeIP 且未屏蔽 AAAA 查询。</value>
</data>
<data name="FillCorrectFakeIPv6Range" xml:space="preserve">
<value>请输入至少包含两个地址的有效 IPv6 网段,例如 fc00::/18。</value>
</data>
<data name="menuColumns" xml:space="preserve">
<value>列</value>
</data>
@@ -1906,4 +1915,4 @@
<data name="LvPreResolve" xml:space="preserve">
<value>预解析节点域名</value>
</data>
</root>
</root>
@@ -1903,4 +1903,13 @@
<data name="TbSortingProcess" xml:space="preserve">
<value>行程</value>
</data>
<data name="TbFakeIPv6" xml:space="preserve">
<value>IPv6 FakeIP</value>
</data>
<data name="TbFakeIPv6Tips" xml:space="preserve">
<value>同時傳回 IPv6 假位址,需啟用 FakeIP 且未封鎖 AAAA 查詢。</value>
</data>
<data name="FillCorrectFakeIPv6Range" xml:space="preserve">
<value>請輸入至少包含兩個位址的有效 IPv6 網段,例如 fc00::/18。</value>
</data>
</root>
@@ -131,6 +131,14 @@ public partial class CoreConfigSingboxService
type = "fakeip",
inet4_range = fakeipRange,
};
if (simpleDnsItem.FakeIPv6 == true && simpleDnsItem.BlockAAAAQuery != true)
{
if (!FakeIPUtils.TryParseIPv6Range(simpleDnsItem.FakeIPv6Range, out var range6, out _))
{
throw new ArgumentException("Invalid IPv6 FakeIP range.");
}
fakeip.inet6_range = range6;
}
_coreConfig.dns.servers.Add(fakeip);
}
}
@@ -145,11 +145,22 @@ public partial class CoreConfigV2rayService
{
// Ignore
}
_coreConfig.fakedns = new()
var fakeDns = new FakeDns4Ray
{
ipPool = fakeipRange,
poolSize = poolSize,
};
_coreConfig.fakedns = JsonUtils.SerializeToNode(fakeDns);
if (_config.SimpleDNSItem.FakeIPv6 == true && _config.SimpleDNSItem.BlockAAAAQuery != true)
{
if (!FakeIPUtils.TryParseIPv6Range(_config.SimpleDNSItem.FakeIPv6Range, out var range6, out var poolSize6))
{
throw new ArgumentException("Invalid IPv6 FakeIP range.");
}
_coreConfig.fakedns = new JsonArray(
_coreConfig.fakedns,
JsonUtils.SerializeToNode(new FakeDns4Ray { ipPool = range6, poolSize = poolSize6 }));
}
}
private void FillDnsServers(Dns4Ray dnsItem)
@@ -8,6 +8,8 @@ public partial class DNSSettingViewModel : MyReactiveObject, ICloseable
[Reactive] public partial bool AddCommonHosts { get; set; }
[Reactive] public partial bool FakeIP { get; set; }
[Reactive] public partial string FakeIPRange { get; set; }
[Reactive] public partial bool FakeIPv6 { get; set; }
[Reactive] public partial string FakeIPv6Range { get; set; }
[Reactive] public partial bool BlockBindingQuery { get; set; }
[Reactive] public partial bool BlockAAAAQuery { get; set; }
[Reactive] public partial string DirectDNS { get; set; }
@@ -36,6 +38,7 @@ public partial class DNSSettingViewModel : MyReactiveObject, ICloseable
[Reactive] public partial bool SBCustomDNSEnableCompatible { get; set; }
[ObservableAsProperty] public partial bool IsSimpleDNSEnabled { get; }
[ObservableAsProperty] public partial bool IsFakeIPv6Available { get; }
public ReactiveCommand<RxVoid, RxVoid> SaveCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> ImportDefConfig4V2rayCompatibleCmd { get; }
@@ -66,6 +69,12 @@ public partial class DNSSettingViewModel : MyReactiveObject, ICloseable
(a, b) => !(a && b))
.ToProperty(this, nameof(IsSimpleDNSEnabled));
_isFakeIPv6AvailableHelper = this.WhenAnyValue(
x => x.FakeIP,
x => x.BlockAAAAQuery,
(fakeIP, blockAAAA) => fakeIP && !blockAAAA)
.ToProperty(this, nameof(IsFakeIPv6Available));
_ = Init();
}
@@ -77,6 +86,8 @@ public partial class DNSSettingViewModel : MyReactiveObject, ICloseable
AddCommonHosts = item.AddCommonHosts ?? false;
FakeIP = item.FakeIP ?? false;
FakeIPRange = item.FakeIPRange ?? string.Empty;
FakeIPv6 = item.FakeIPv6 ?? false;
FakeIPv6Range = item.FakeIPv6Range ?? Global.FakeIPv6Ranges.First();
BlockBindingQuery = item.BlockBindingQuery ?? false;
BlockAAAAQuery = item.BlockAAAAQuery ?? false;
DirectDNS = item.DirectDNS ?? string.Empty;
@@ -108,10 +119,19 @@ public partial class DNSSettingViewModel : MyReactiveObject, ICloseable
private async Task SaveSettingAsync()
{
if (IsSimpleDNSEnabled && IsFakeIPv6Available && FakeIPv6
&& !FakeIPUtils.TryParseIPv6Range(FakeIPv6Range, out _, out _))
{
NoticeManager.Instance.Enqueue(ResUI.FillCorrectFakeIPv6Range);
return;
}
_config.SimpleDNSItem.UseSystemHosts = UseSystemHosts;
_config.SimpleDNSItem.AddCommonHosts = AddCommonHosts;
_config.SimpleDNSItem.FakeIP = FakeIP;
_config.SimpleDNSItem.FakeIPRange = FakeIPRange;
_config.SimpleDNSItem.FakeIPv6 = FakeIPv6;
_config.SimpleDNSItem.FakeIPv6Range = FakeIPv6Range;
_config.SimpleDNSItem.BlockBindingQuery = BlockBindingQuery;
_config.SimpleDNSItem.BlockAAAAQuery = BlockAAAAQuery;
_config.SimpleDNSItem.DirectDNS = DirectDNS;
@@ -242,7 +242,7 @@
x:Name="gridAdvancedDNSSettings"
Margin="{StaticResource Margin8}"
ColumnDefinitions="Auto,Auto,*"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,*">
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,*">
<TextBlock
x:Name="txtAdvancedDNSSettingsInvalid"
@@ -317,16 +317,49 @@
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbFakeIPv6}" />
<Grid x:Name="gridFakeIPv6" Grid.Row="4" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ToggleSwitch
x:Name="togFakeIPv6"
Grid.Column="0"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<ComboBox
x:Name="cmbFakeIPv6Range"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
IsEditable="True" />
</Grid>
<TextBlock
Grid.Row="4"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbFakeIPv6Tips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
<ToggleSwitch
x:Name="togBlockBindingQuery"
Grid.Row="4"
Grid.Row="5"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
@@ -334,20 +367,20 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbBlockAAAAQueries}" />
<ToggleSwitch
x:Name="togBlockAAAAQuery"
Grid.Row="5"
Grid.Row="6"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
@@ -355,21 +388,21 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="6"
Grid.Row="7"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
<ComboBox
x:Name="cmbDirectExpectedIPs"
Grid.Row="6"
Grid.Row="7"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsEditable="True" />
<TextBlock
Grid.Row="6"
Grid.Row="7"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
@@ -377,7 +410,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="7"
Grid.Row="8"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin4}"
@@ -386,7 +419,7 @@
<TextBox
x:Name="txtHosts"
Grid.Row="8"
Grid.Row="9"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin4}"
@@ -21,6 +21,7 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
cmbFakeIPRange.ItemsSource = Global.FakeIPRanges;
cmbFakeIPv6Range.ItemsSource = Global.FakeIPv6Ranges;
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy;
@@ -34,6 +35,9 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIPRange, v => v.cmbFakeIPRange.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIPv6, v => v.togFakeIPv6.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIPv6Range, v => v.cmbFakeIPv6Range.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.IsFakeIPv6Available, v => v.gridFakeIPv6.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BlockAAAAQuery, v => v.togBlockAAAAQuery.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
+47 -10
View File
@@ -279,6 +279,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@@ -366,15 +367,51 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbFakeIPv6}" />
<Grid x:Name="gridFakeIPv6" Grid.Row="4" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ToggleButton
x:Name="togFakeIPv6"
Grid.Column="0"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<ComboBox
x:Name="cmbFakeIPv6Range"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
IsEditable="True"
Style="{StaticResource DefComboBox}" />
</Grid>
<TextBlock
Grid.Row="4"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbFakeIPv6Tips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
<ToggleButton
x:Name="togBlockBindingQuery"
Grid.Row="4"
Grid.Row="5"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -383,7 +420,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -391,12 +428,12 @@
Text="{x:Static resx:ResUI.TbBlockAAAAQueries}" />
<ToggleButton
x:Name="togBlockAAAAQuery"
Grid.Row="5"
Grid.Row="6"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -405,7 +442,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="6"
Grid.Row="7"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -413,14 +450,14 @@
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
<ComboBox
x:Name="cmbDirectExpectedIPs"
Grid.Row="6"
Grid.Row="7"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
IsEditable="True"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="6"
Grid.Row="7"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -429,7 +466,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="7"
Grid.Row="8"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin8}"
@@ -438,7 +475,7 @@
Text="{x:Static resx:ResUI.TbDNSHostsConfig}" />
<TextBox
x:Name="txtHosts"
Grid.Row="8"
Grid.Row="9"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin8}"
@@ -17,6 +17,7 @@ public partial class DNSSettingWindow
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
cmbFakeIPRange.ItemsSource = Global.FakeIPRanges;
cmbFakeIPv6Range.ItemsSource = Global.FakeIPv6Ranges;
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy;
@@ -30,6 +31,9 @@ public partial class DNSSettingWindow
this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIPRange, v => v.cmbFakeIPRange.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIPv6, v => v.togFakeIPv6.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIPv6Range, v => v.cmbFakeIPv6Range.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.IsFakeIPv6Available, v => v.gridFakeIPv6.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BlockAAAAQuery, v => v.togBlockAAAAQuery.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);