Optimize ExpectedIPs Logic

DHR60 2025-07-22 18:09:15 +08:00
parent 5331e2eeff
commit 4105031446
10 changed files with 220 additions and 227 deletions

View File

@ -1,4 +1,4 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 此代码由工具生成。 // 此代码由工具生成。
// 运行时版本:4.0.30319.42000 // 运行时版本:4.0.30319.42000
@ -3832,7 +3832,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Validate Direct Expected IPs 的本地化字符串。 /// 查找类似 Validate Regional Domain IPs 的本地化字符串。
/// </summary> /// </summary>
public static string TbValidateDirectExpectedIPs { public static string TbValidateDirectExpectedIPs {
get { get {
@ -3841,7 +3841,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 After configuration, validates returned IPs, returning only expected IPs 的本地化字符串。 /// 查找类似 When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs 的本地化字符串。
/// </summary> /// </summary>
public static string TbValidateDirectExpectedIPsDesc { public static string TbValidateDirectExpectedIPsDesc {
get { get {

View File

@ -1456,9 +1456,9 @@
<value>Advanced DNS Settings</value> <value>Advanced DNS Settings</value>
</data> </data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve"> <data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>Validate Direct Expected IPs</value> <value>Validate Regional Domain IPs</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>After configuration, validates returned IPs, returning only expected IPs</value> <value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
</data> </data>
</root> </root>

View File

@ -1456,9 +1456,9 @@
<value>Advanced DNS Settings</value> <value>Advanced DNS Settings</value>
</data> </data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve"> <data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>Validate Direct Expected IPs</value> <value>Validate Regional Domain IPs</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>After configuration, validates returned IPs, returning only expected IPs</value> <value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
</data> </data>
</root> </root>

View File

@ -1456,9 +1456,9 @@
<value>Advanced DNS Settings</value> <value>Advanced DNS Settings</value>
</data> </data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve"> <data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>Validate Direct Expected IPs</value> <value>Validate Regional Domain IPs</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>After configuration, validates returned IPs, returning only expected IPs</value> <value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
</data> </data>
</root> </root>

View File

@ -1456,9 +1456,9 @@
<value>Advanced DNS Settings</value> <value>Advanced DNS Settings</value>
</data> </data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve"> <data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>Validate Direct Expected IPs</value> <value>Validate Regional Domain IPs</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>After configuration, validates returned IPs, returning only expected IPs</value> <value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
</data> </data>
</root> </root>

View File

@ -1453,9 +1453,9 @@
<value>DNS 进阶设置</value> <value>DNS 进阶设置</value>
</data> </data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve"> <data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>校验直连期望 IP</value> <value>校验相应地区域名 IP</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>配置后,会对返回的 IP 的进行校验,只返回期望 IP</value> <value>配置后,会对相应地区域名(如 geosite:cn的返回 IP 进行校验,仅返回期望 IP</value>
</data> </data>
</root> </root>

View File

@ -1453,9 +1453,9 @@
<value>Advanced DNS Settings</value> <value>Advanced DNS Settings</value>
</data> </data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve"> <data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>Validate Direct Expected IPs</value> <value>Validate Regional Domain IPs</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>After configuration, validates returned IPs, returning only expected IPs</value> <value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
</data> </data>
</root> </root>

View File

@ -1704,68 +1704,100 @@ public class CoreConfigSingboxService
{ {
singboxConfig.dns ??= new Dns4Sbox(); singboxConfig.dns ??= new Dns4Sbox();
singboxConfig.dns.rules ??= new List<Rule4Sbox>(); singboxConfig.dns.rules ??= new List<Rule4Sbox>();
// hosts
singboxConfig.dns.rules.Add(new Rule4Sbox singboxConfig.dns.rules.AddRange(new[]
{ {
ip_accept_any = true, new Rule4Sbox { ip_accept_any = true, server = "dns_hosts" },
server = "dns_hosts", new Rule4Sbox
});
// clash mode
singboxConfig.dns.rules.Add(new Rule4Sbox
{ {
server = "dns_remote", server = "dns_remote",
strategy = dNSItem.SingboxStrategy4Proxy.IsNullOrEmpty() ? null : dNSItem.SingboxStrategy4Proxy, strategy = string.IsNullOrEmpty(dNSItem.SingboxStrategy4Proxy) ? null : dNSItem.SingboxStrategy4Proxy,
clash_mode = ERuleMode.Global.ToString() clash_mode = ERuleMode.Global.ToString()
}); },
singboxConfig.dns.rules.Add(new Rule4Sbox new Rule4Sbox
{ {
server = "dns_direct", server = "dns_direct",
strategy = dNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : dNSItem.SingboxStrategy4Direct, strategy = string.IsNullOrEmpty(dNSItem.SingboxStrategy4Direct) ? null : dNSItem.SingboxStrategy4Direct,
clash_mode = ERuleMode.Direct.ToString() clash_mode = ERuleMode.Direct.ToString()
}); },
// block binding query new Rule4Sbox
singboxConfig.dns.rules.Add(new Rule4Sbox
{ {
query_type = new List<int> { 64, 65 }, query_type = new List<int> { 64, 65 },
action = "predefined", action = "predefined",
rcode = "NOTIMP" rcode = "NOTIMP"
}
}); });
var routing = await ConfigHandler.GetDefaultRouting(_config); var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing != null) if (routing == null)
{ return 0;
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
foreach (var item in rules ?? [])
{
if (!item.Enabled)
{
continue;
}
if (item.Domain == null || item.Domain.Count == 0)
{
continue;
}
var rule = new Rule4Sbox();
var countDomain = 0; var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
foreach (var it in item.Domain) var expectedIPCidr = new List<string>();
var expectedIPsRegions = new List<string>();
var regionNames = new HashSet<string>();
if (!string.IsNullOrEmpty(dNSItem?.DirectExpectedIPs))
{ {
if (ParseV2Domain(it, rule)) var ipItems = dNSItem.DirectExpectedIPs
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.Where(s => !string.IsNullOrEmpty(s))
.ToList();
foreach (var ip in ipItems)
{ {
countDomain++; if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase))
{
var region = ip["geoip:".Length..];
if (!string.IsNullOrEmpty(region))
{
expectedIPsRegions.Add(region);
regionNames.Add(region);
regionNames.Add($"geolocation-{region}");
regionNames.Add($"tld-{region}");
} }
} }
if (countDomain <= 0) else
{
expectedIPCidr.Add(ip);
}
}
}
foreach (var item in rules)
{
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
{ {
continue; continue;
} }
var rule = new Rule4Sbox();
var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule));
if (validDomains <= 0)
{
continue;
}
if (item.OutboundTag == Global.DirectTag) if (item.OutboundTag == Global.DirectTag)
{ {
rule.server = "dns_direct"; rule.server = "dns_direct";
rule.strategy = dNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : dNSItem.SingboxStrategy4Direct; rule.strategy = string.IsNullOrEmpty(dNSItem.SingboxStrategy4Direct) ? null : dNSItem.SingboxStrategy4Direct;
if (!dNSItem.DirectExpectedIPs.IsNullOrEmpty())
if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0)
{ {
rule.rule_set = new() { dNSItem.DirectExpectedIPs }; var geositeSet = new HashSet<string>(rule.geosite);
if (regionNames.Intersect(geositeSet).Any())
{
if (expectedIPsRegions.Count > 0)
{
rule.geoip = expectedIPsRegions;
}
if (expectedIPCidr.Count > 0)
{
rule.ip_cidr = expectedIPCidr;
}
}
} }
} }
else if (item.OutboundTag == Global.ProxyTag) else if (item.OutboundTag == Global.ProxyTag)
@ -1777,7 +1809,7 @@ public class CoreConfigSingboxService
singboxConfig.dns.rules.Add(rule4Fake); singboxConfig.dns.rules.Add(rule4Fake);
} }
rule.server = "dns_remote"; rule.server = "dns_remote";
rule.strategy = dNSItem.SingboxStrategy4Proxy.IsNullOrEmpty() ? null : dNSItem.SingboxStrategy4Proxy; rule.strategy = string.IsNullOrEmpty(dNSItem.SingboxStrategy4Proxy) ? null : dNSItem.SingboxStrategy4Proxy;
} }
else if (item.OutboundTag == Global.BlockTag) else if (item.OutboundTag == Global.BlockTag)
{ {
@ -1785,11 +1817,11 @@ public class CoreConfigSingboxService
rule.rcode = "NOERROR"; rule.rcode = "NOERROR";
rule.answer = new List<string> { "A" }; rule.answer = new List<string> { "A" };
} }
singboxConfig.dns.rules.Add(rule); singboxConfig.dns.rules.Add(rule);
} }
}
return await Task.FromResult(0); return 0;
} }
private static Server4Sbox? ParseDnsAddress(string address) private static Server4Sbox? ParseDnsAddress(string address)

View File

@ -1167,194 +1167,155 @@ public class CoreConfigV2rayService
private async Task<int> GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, DNSItem dNSItem) private async Task<int> GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, DNSItem dNSItem)
{ {
var directDNSAddress = dNSItem?.DirectDNS? static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
.Split(dNSItem.DirectDNS?.Contains(',') == true ? ',' : ';') {
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
.Select(addr => addr.Trim()) .Select(addr => addr.Trim())
.Where(addr => !string.IsNullOrEmpty(addr)) .Where(addr => !string.IsNullOrEmpty(addr))
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr) .Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
.Distinct() .Distinct()
.ToList(); .ToList() ?? new List<string> { defaultAddress };
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
if (directDNSAddress != null && directDNSAddress.Count == 0)
{
directDNSAddress = new() { Global.DomainDirectDNSAddress.FirstOrDefault() };
} }
var remoteDNSAddress = dNSItem?.RemoteDNS? static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
.Split(dNSItem.RemoteDNS?.Contains(',') == true ? ',' : ';')
.Select(addr => addr.Trim())
.Where(addr => !string.IsNullOrEmpty(addr))
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
.Distinct()
.ToList();
if (remoteDNSAddress != null && remoteDNSAddress.Count == 0)
{ {
remoteDNSAddress = new() { Global.DomainRemoteDNSAddress.FirstOrDefault() }; var dnsServer = new DnsServer4Ray
{
address = dnsAddress,
skipFallback = true,
domains = domains.Count > 0 ? domains : null,
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
};
return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
});
} }
var directDNSAddress = ParseDnsAddresses(dNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault());
var remoteDNSAddress = ParseDnsAddresses(dNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault());
var directDomainList = new List<string>(); var directDomainList = new List<string>();
var directGeositeList = new List<string>(); var directGeositeList = new List<string>();
var proxyDomainList = new List<string>(); var proxyDomainList = new List<string>();
var proxyGeositeList = new List<string>(); var proxyGeositeList = new List<string>();
var expectedDomainList = new List<string>();
var expectedIPs = new List<string>();
var regionNames = new HashSet<string>();
if (!string.IsNullOrEmpty(dNSItem?.DirectExpectedIPs))
{
expectedIPs = dNSItem.DirectExpectedIPs
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.Where(s => !string.IsNullOrEmpty(s))
.ToList();
foreach (var ip in expectedIPs)
{
if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase))
{
var region = ip["geoip:".Length..];
if (!string.IsNullOrEmpty(region))
{
regionNames.Add($"geosite:{region}");
regionNames.Add($"geosite:geolocation-{region}");
regionNames.Add($"geosite:tld-{region}");
}
}
}
}
var routing = await ConfigHandler.GetDefaultRouting(_config); var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing != null) if (routing != null)
{ {
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
foreach (var item in rules ?? []) foreach (var item in rules)
{ {
if (!item.Enabled) if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
{ {
continue; continue;
} }
if (item.Domain == null || item.Domain.Count == 0)
{
continue;
}
if (item.OutboundTag == Global.DirectTag)
{
foreach (var domain in item.Domain) foreach (var domain in item.Domain)
{ {
if (domain.StartsWith('#')) if (domain.StartsWith('#'))
{
continue; continue;
} var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
var domain1 = domain.Replace(Global.RoutingRuleComma, ",");
if (domain1.StartsWith("geosite:")) if (item.OutboundTag == Global.DirectTag)
{ {
directGeositeList.Add(domain1); if (normalizedDomain.StartsWith("geosite:"))
{
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
} }
else else
{ {
directDomainList.Add(domain1); directDomainList.Add(normalizedDomain);
}
} }
} }
else if (item.OutboundTag == Global.ProxyTag) else if (item.OutboundTag == Global.ProxyTag)
{ {
foreach (var domain in item.Domain) if (normalizedDomain.StartsWith("geosite:"))
{ {
if (domain.StartsWith('#')) proxyGeositeList.Add(normalizedDomain);
{
continue;
}
var domain1 = domain.Replace(Global.RoutingRuleComma, ",");
if (domain1.StartsWith("geosite:"))
{
proxyGeositeList.Add(domain1);
} }
else else
{ {
proxyDomainList.Add(domain1); proxyDomainList.Add(normalizedDomain);
} }
} }
} }
} }
} }
if (Utils.IsDomain(node.Address)) if (Utils.IsDomain(node?.Address))
{ {
directDomainList.Add(node.Address); directDomainList.Add(node.Address);
} }
if (node?.Subid is not null)
{
var subItem = await AppHandler.Instance.GetSubItem(node.Subid); var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
if (subItem is not null) if (subItem is not null)
{ {
// Previous proxy foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile })
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
if (prevNode is not null
&& prevNode.ConfigType != EConfigType.Custom
&& prevNode.ConfigType != EConfigType.Hysteria2
&& prevNode.ConfigType != EConfigType.TUIC
&& prevNode.ConfigType != EConfigType.Anytls
&& Utils.IsDomain(prevNode.Address))
{ {
directDomainList.Add(prevNode.Address); var profileNode = await AppHandler.Instance.GetProfileItemViaRemarks(profile);
if (profileNode is not null &&
profileNode.ConfigType is not (EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) &&
Utils.IsDomain(profileNode.Address))
{
directDomainList.Add(profileNode.Address);
}
} }
// Next proxy
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
if (nextNode is not null
&& nextNode.ConfigType != EConfigType.Custom
&& nextNode.ConfigType != EConfigType.Hysteria2
&& nextNode.ConfigType != EConfigType.TUIC
&& nextNode.ConfigType != EConfigType.Anytls
&& Utils.IsDomain(nextNode.Address))
{
directDomainList.Add(nextNode.Address);
} }
} }
v2rayConfig.dns ??= new Dns4Ray(); v2rayConfig.dns ??= new Dns4Ray();
v2rayConfig.dns.servers ??= new List<object>(); v2rayConfig.dns.servers ??= new List<object>();
var options = new JsonSerializerOptions void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null)
{ {
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull if (domains.Count > 0)
};
if (proxyDomainList.Count > 0)
{ {
foreach (var dnsDomain in remoteDNSAddress) foreach (var dnsAddress in dnsAddresses)
{ {
var dnsServer = new DnsServer4Ray v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
{ }
address = dnsDomain,
skipFallback = true,
domains = proxyDomainList
};
v2rayConfig.dns.servers.Add(JsonUtils.SerializeToNode(dnsServer, options));
} }
} }
if (directDomainList.Count > 0) AddDnsServers(remoteDNSAddress, proxyDomainList);
{ AddDnsServers(directDNSAddress, directDomainList);
foreach (var dnsDomain in directDNSAddress) AddDnsServers(remoteDNSAddress, proxyGeositeList);
{ AddDnsServers(directDNSAddress, directGeositeList);
var dnsServer = new DnsServer4Ray AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
{
address = dnsDomain,
skipFallback = true,
domains = directDomainList,
expectedIPs = dNSItem.DirectExpectedIPs.IsNullOrEmpty() ? null : new() { dNSItem.DirectExpectedIPs }
};
v2rayConfig.dns.servers.Add(JsonUtils.SerializeToNode(dnsServer, options));
}
}
if (proxyGeositeList.Count > 0)
{
foreach (var dnsDomain in remoteDNSAddress)
{
var dnsServer = new DnsServer4Ray()
{
address = dnsDomain,
skipFallback = true,
domains = proxyGeositeList
};
v2rayConfig.dns.servers.Add(JsonUtils.SerializeToNode(dnsServer, options));
}
}
if (directGeositeList.Count > 0)
{
foreach (var dnsDomain in directDNSAddress)
{
var dnsServer = new DnsServer4Ray()
{
address = dnsDomain,
skipFallback = true,
domains = directGeositeList,
expectedIPs = dNSItem.DirectExpectedIPs.IsNullOrEmpty() ? null : new() { dNSItem.DirectExpectedIPs }
};
v2rayConfig.dns.servers.Add(JsonUtils.SerializeToNode(dnsServer, options));
}
}
// fallback DNS server v2rayConfig.dns.servers.AddRange(remoteDNSAddress);
// TODO: Select fallback DNS server based on routing rules
foreach (var dnsDomain in remoteDNSAddress) return 0;
{
v2rayConfig.dns.servers.Add(dnsDomain);
}
return await Task.FromResult(0);
} }
private async Task<int> GenDnsHosts(V2rayConfig v2rayConfig, DNSItem dNSItem) private async Task<int> GenDnsHosts(V2rayConfig v2rayConfig, DNSItem dNSItem)

View File

@ -32,15 +32,15 @@ public partial class DNSSettingWindow
this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables); 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.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });