mirror of
https://github.com/2dust/v2rayN.git
synced 2026-03-12 19:33:01 +00:00
Fix hosts matching (#8890)
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
* Fix hosts matching * Fix hosts resolve rule * Fix
This commit is contained in:
parent
81da72bb39
commit
b8f7cc0768
2 changed files with 123 additions and 33 deletions
|
|
@ -93,7 +93,23 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
||||||
{
|
{
|
||||||
hostsDns.predefined[kvp.Key] = kvp.Value.Where(Utils.IsIpAddress).ToList();
|
// only allow full match
|
||||||
|
// like example.com and full:example.com,
|
||||||
|
// but not domain:example.com, keyword:example.com or regex:example.com etc.
|
||||||
|
var testRule = new Rule4Sbox();
|
||||||
|
if (!ParseV2Domain(kvp.Key, testRule))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (testRule.domain_keyword?.Count > 0 && !kvp.Key.Contains(':'))
|
||||||
|
{
|
||||||
|
testRule.domain = testRule.domain_keyword;
|
||||||
|
testRule.domain_keyword = null;
|
||||||
|
}
|
||||||
|
if (testRule.domain?.Count == 1)
|
||||||
|
{
|
||||||
|
hostsDns.predefined[testRule.domain.First()] = kvp.Value.Where(Utils.IsIpAddress).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var host in hostsDns.predefined)
|
foreach (var host in hostsDns.predefined)
|
||||||
|
|
@ -179,44 +195,66 @@ public partial class CoreConfigSingboxService
|
||||||
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
||||||
{
|
{
|
||||||
var predefined = kvp.Value.First();
|
var predefined = kvp.Value.First();
|
||||||
if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined))
|
if (predefined.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode))
|
var rule = new Rule4Sbox()
|
||||||
{
|
{
|
||||||
// xray syntactic sugar for predefined
|
query_type = [1, 5, 28], // A, CNAME and AAAA
|
||||||
// etc. #0 -> NOERROR
|
|
||||||
_coreConfig.dns.rules.Add(new()
|
|
||||||
{
|
|
||||||
query_type = [1, 28],
|
|
||||||
domain = [kvp.Key],
|
|
||||||
action = "predefined",
|
|
||||||
rcode = rcode switch
|
|
||||||
{
|
|
||||||
0 => "NOERROR",
|
|
||||||
1 => "FORMERR",
|
|
||||||
2 => "SERVFAIL",
|
|
||||||
3 => "NXDOMAIN",
|
|
||||||
4 => "NOTIMP",
|
|
||||||
5 => "REFUSED",
|
|
||||||
_ => "NOERROR",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// CNAME record
|
|
||||||
Rule4Sbox rule = new()
|
|
||||||
{
|
|
||||||
query_type = [1, 28],
|
|
||||||
action = "predefined",
|
action = "predefined",
|
||||||
rcode = "NOERROR",
|
rcode = "NOERROR",
|
||||||
answer = [$"*. IN CNAME {predefined}."],
|
|
||||||
};
|
};
|
||||||
if (ParseV2Domain(kvp.Key, rule))
|
if (!ParseV2Domain(kvp.Key, rule))
|
||||||
{
|
{
|
||||||
_coreConfig.dns.rules.Add(rule);
|
continue;
|
||||||
}
|
}
|
||||||
|
// see: https://xtls.github.io/en/config/dns.html#dnsobject
|
||||||
|
// The matching format (domain:, full:, etc.) is the same as the domain
|
||||||
|
// in the commonly used Routing System. The difference is that without a prefix,
|
||||||
|
// it defaults to using the full: prefix (similar to the common hosts file syntax).
|
||||||
|
if (rule.domain_keyword?.Count > 0 && !kvp.Key.Contains(':'))
|
||||||
|
{
|
||||||
|
rule.domain = rule.domain_keyword;
|
||||||
|
rule.domain_keyword = null;
|
||||||
|
}
|
||||||
|
// example.com #0 -> example.com with NOERROR
|
||||||
|
if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode))
|
||||||
|
{
|
||||||
|
rule.rcode = rcode switch
|
||||||
|
{
|
||||||
|
0 => "NOERROR",
|
||||||
|
1 => "FORMERR",
|
||||||
|
2 => "SERVFAIL",
|
||||||
|
3 => "NXDOMAIN",
|
||||||
|
4 => "NOTIMP",
|
||||||
|
5 => "REFUSED",
|
||||||
|
_ => "NOERROR",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (Utils.IsDomain(predefined))
|
||||||
|
{
|
||||||
|
// example.com CNAME target.com -> example.com with CNAME target.com
|
||||||
|
rule.answer = new List<string> { $"*. IN CNAME {predefined}." };
|
||||||
|
}
|
||||||
|
else if (Utils.IsIpAddress(predefined) && (rule.domain?.Count ?? 0) == 0)
|
||||||
|
{
|
||||||
|
// not full match, but an IP address, treat it as predefined answer
|
||||||
|
if (Utils.IsIpv6(predefined))
|
||||||
|
{
|
||||||
|
rule.answer = new List<string> { $"*. IN AAAA {predefined}" };
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rule.answer = new List<string> { $"*. IN A {predefined}" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_coreConfig.dns.rules.Add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simpleDnsItem.BlockBindingQuery == true)
|
if (simpleDnsItem.BlockBindingQuery == true)
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,58 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
if (hostsDomains.Count > 0)
|
if (hostsDomains.Count > 0)
|
||||||
{
|
{
|
||||||
_coreConfig.route.rules.Add(new()
|
var hostsResolveRule = new Rule4Sbox
|
||||||
{
|
{
|
||||||
action = "resolve",
|
action = "resolve",
|
||||||
domain = hostsDomains,
|
};
|
||||||
});
|
var hostsCounter = 0;
|
||||||
|
foreach (var host in hostsDomains)
|
||||||
|
{
|
||||||
|
var domainRule = new Rule4Sbox();
|
||||||
|
if (!ParseV2Domain(host, domainRule))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (domainRule.domain_keyword?.Count > 0 && !host.Contains(':'))
|
||||||
|
{
|
||||||
|
domainRule.domain = domainRule.domain_keyword;
|
||||||
|
domainRule.domain_keyword = null;
|
||||||
|
}
|
||||||
|
if (domainRule.domain?.Count > 0)
|
||||||
|
{
|
||||||
|
hostsResolveRule.domain ??= [];
|
||||||
|
hostsResolveRule.domain.AddRange(domainRule.domain);
|
||||||
|
hostsCounter++;
|
||||||
|
}
|
||||||
|
else if (domainRule.domain_keyword?.Count > 0)
|
||||||
|
{
|
||||||
|
hostsResolveRule.domain_keyword ??= [];
|
||||||
|
hostsResolveRule.domain_keyword.AddRange(domainRule.domain_keyword);
|
||||||
|
hostsCounter++;
|
||||||
|
}
|
||||||
|
else if (domainRule.domain_suffix?.Count > 0)
|
||||||
|
{
|
||||||
|
hostsResolveRule.domain_suffix ??= [];
|
||||||
|
hostsResolveRule.domain_suffix.AddRange(domainRule.domain_suffix);
|
||||||
|
hostsCounter++;
|
||||||
|
}
|
||||||
|
else if (domainRule.domain_regex?.Count > 0)
|
||||||
|
{
|
||||||
|
hostsResolveRule.domain_regex ??= [];
|
||||||
|
hostsResolveRule.domain_regex.AddRange(domainRule.domain_regex);
|
||||||
|
hostsCounter++;
|
||||||
|
}
|
||||||
|
else if (domainRule.geosite?.Count > 0)
|
||||||
|
{
|
||||||
|
hostsResolveRule.geosite ??= [];
|
||||||
|
hostsResolveRule.geosite.AddRange(domainRule.geosite);
|
||||||
|
hostsCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hostsCounter > 0)
|
||||||
|
{
|
||||||
|
_coreConfig.route.rules.Add(hostsResolveRule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_coreConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
|
|
@ -355,6 +402,11 @@ public partial class CoreConfigSingboxService
|
||||||
rule.domain_keyword ??= [];
|
rule.domain_keyword ??= [];
|
||||||
rule.domain_keyword?.Add(domain.Substring(8));
|
rule.domain_keyword?.Add(domain.Substring(8));
|
||||||
}
|
}
|
||||||
|
else if (domain.StartsWith("dotless:"))
|
||||||
|
{
|
||||||
|
rule.domain_keyword ??= [];
|
||||||
|
rule.domain_keyword?.Add(domain.Substring(8));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rule.domain_keyword ??= [];
|
rule.domain_keyword ??= [];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue