Skip to content

Commit b8f7cc0

Browse files
authored
Fix hosts matching (#8890)
* Fix hosts matching * Fix hosts resolve rule * Fix
1 parent 81da72b commit b8f7cc0

File tree

2 files changed

+122
-32
lines changed

2 files changed

+122
-32
lines changed

v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,23 @@ private void GenDnsServers()
9393

9494
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
9595
{
96-
hostsDns.predefined[kvp.Key] = kvp.Value.Where(Utils.IsIpAddress).ToList();
96+
// only allow full match
97+
// like example.com and full:example.com,
98+
// but not domain:example.com, keyword:example.com or regex:example.com etc.
99+
var testRule = new Rule4Sbox();
100+
if (!ParseV2Domain(kvp.Key, testRule))
101+
{
102+
continue;
103+
}
104+
if (testRule.domain_keyword?.Count > 0 && !kvp.Key.Contains(':'))
105+
{
106+
testRule.domain = testRule.domain_keyword;
107+
testRule.domain_keyword = null;
108+
}
109+
if (testRule.domain?.Count == 1)
110+
{
111+
hostsDns.predefined[testRule.domain.First()] = kvp.Value.Where(Utils.IsIpAddress).ToList();
112+
}
97113
}
98114

99115
foreach (var host in hostsDns.predefined)
@@ -179,44 +195,66 @@ private void GenDnsRules()
179195
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
180196
{
181197
var predefined = kvp.Value.First();
182-
if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined))
198+
if (predefined.IsNullOrEmpty())
199+
{
200+
continue;
201+
}
202+
var rule = new Rule4Sbox()
203+
{
204+
query_type = [1, 5, 28], // A, CNAME and AAAA
205+
action = "predefined",
206+
rcode = "NOERROR",
207+
};
208+
if (!ParseV2Domain(kvp.Key, rule))
183209
{
184210
continue;
185211
}
212+
// see: https://xtls.github.io/en/config/dns.html#dnsobject
213+
// The matching format (domain:, full:, etc.) is the same as the domain
214+
// in the commonly used Routing System. The difference is that without a prefix,
215+
// it defaults to using the full: prefix (similar to the common hosts file syntax).
216+
if (rule.domain_keyword?.Count > 0 && !kvp.Key.Contains(':'))
217+
{
218+
rule.domain = rule.domain_keyword;
219+
rule.domain_keyword = null;
220+
}
221+
// example.com #0 -> example.com with NOERROR
186222
if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode))
187223
{
188-
// xray syntactic sugar for predefined
189-
// etc. #0 -> NOERROR
190-
_coreConfig.dns.rules.Add(new()
224+
rule.rcode = rcode switch
191225
{
192-
query_type = [1, 28],
193-
domain = [kvp.Key],
194-
action = "predefined",
195-
rcode = rcode switch
196-
{
197-
0 => "NOERROR",
198-
1 => "FORMERR",
199-
2 => "SERVFAIL",
200-
3 => "NXDOMAIN",
201-
4 => "NOTIMP",
202-
5 => "REFUSED",
203-
_ => "NOERROR",
204-
},
205-
});
206-
continue;
226+
0 => "NOERROR",
227+
1 => "FORMERR",
228+
2 => "SERVFAIL",
229+
3 => "NXDOMAIN",
230+
4 => "NOTIMP",
231+
5 => "REFUSED",
232+
_ => "NOERROR",
233+
};
207234
}
208-
// CNAME record
209-
Rule4Sbox rule = new()
235+
else if (Utils.IsDomain(predefined))
210236
{
211-
query_type = [1, 28],
212-
action = "predefined",
213-
rcode = "NOERROR",
214-
answer = [$"*. IN CNAME {predefined}."],
215-
};
216-
if (ParseV2Domain(kvp.Key, rule))
237+
// example.com CNAME target.com -> example.com with CNAME target.com
238+
rule.answer = new List<string> { $"*. IN CNAME {predefined}." };
239+
}
240+
else if (Utils.IsIpAddress(predefined) && (rule.domain?.Count ?? 0) == 0)
241+
{
242+
// not full match, but an IP address, treat it as predefined answer
243+
if (Utils.IsIpv6(predefined))
244+
{
245+
rule.answer = new List<string> { $"*. IN AAAA {predefined}" };
246+
247+
}
248+
else
249+
{
250+
rule.answer = new List<string> { $"*. IN A {predefined}" };
251+
}
252+
}
253+
else
217254
{
218-
_coreConfig.dns.rules.Add(rule);
255+
continue;
219256
}
257+
_coreConfig.dns.rules.Add(rule);
220258
}
221259

222260
if (simpleDnsItem.BlockBindingQuery == true)

v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,58 @@ private void GenRouting()
8484
}
8585
if (hostsDomains.Count > 0)
8686
{
87-
_coreConfig.route.rules.Add(new()
87+
var hostsResolveRule = new Rule4Sbox
8888
{
8989
action = "resolve",
90-
domain = hostsDomains,
91-
});
90+
};
91+
var hostsCounter = 0;
92+
foreach (var host in hostsDomains)
93+
{
94+
var domainRule = new Rule4Sbox();
95+
if (!ParseV2Domain(host, domainRule))
96+
{
97+
continue;
98+
}
99+
if (domainRule.domain_keyword?.Count > 0 && !host.Contains(':'))
100+
{
101+
domainRule.domain = domainRule.domain_keyword;
102+
domainRule.domain_keyword = null;
103+
}
104+
if (domainRule.domain?.Count > 0)
105+
{
106+
hostsResolveRule.domain ??= [];
107+
hostsResolveRule.domain.AddRange(domainRule.domain);
108+
hostsCounter++;
109+
}
110+
else if (domainRule.domain_keyword?.Count > 0)
111+
{
112+
hostsResolveRule.domain_keyword ??= [];
113+
hostsResolveRule.domain_keyword.AddRange(domainRule.domain_keyword);
114+
hostsCounter++;
115+
}
116+
else if (domainRule.domain_suffix?.Count > 0)
117+
{
118+
hostsResolveRule.domain_suffix ??= [];
119+
hostsResolveRule.domain_suffix.AddRange(domainRule.domain_suffix);
120+
hostsCounter++;
121+
}
122+
else if (domainRule.domain_regex?.Count > 0)
123+
{
124+
hostsResolveRule.domain_regex ??= [];
125+
hostsResolveRule.domain_regex.AddRange(domainRule.domain_regex);
126+
hostsCounter++;
127+
}
128+
else if (domainRule.geosite?.Count > 0)
129+
{
130+
hostsResolveRule.geosite ??= [];
131+
hostsResolveRule.geosite.AddRange(domainRule.geosite);
132+
hostsCounter++;
133+
}
134+
}
135+
if (hostsCounter > 0)
136+
{
137+
_coreConfig.route.rules.Add(hostsResolveRule);
138+
}
92139
}
93140

94141
_coreConfig.route.rules.Add(new()
@@ -355,6 +402,11 @@ private static bool ParseV2Domain(string domain, Rule4Sbox rule)
355402
rule.domain_keyword ??= [];
356403
rule.domain_keyword?.Add(domain.Substring(8));
357404
}
405+
else if (domain.StartsWith("dotless:"))
406+
{
407+
rule.domain_keyword ??= [];
408+
rule.domain_keyword?.Add(domain.Substring(8));
409+
}
358410
else
359411
{
360412
rule.domain_keyword ??= [];

0 commit comments

Comments
 (0)