mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-18 08:13:02 +00:00
Add DNS Hosts features (#8756)
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
This commit is contained in:
parent
bceebc1661
commit
d9843dc775
4 changed files with 70 additions and 44 deletions
|
|
@ -332,22 +332,20 @@ public class Utils
|
|||
.ToList();
|
||||
}
|
||||
|
||||
public static Dictionary<string, List<string>> ParseHostsToDictionary(string hostsContent)
|
||||
public static Dictionary<string, List<string>> ParseHostsToDictionary(string? hostsContent)
|
||||
{
|
||||
if (hostsContent.IsNullOrEmpty())
|
||||
{
|
||||
return new();
|
||||
}
|
||||
var userHostsMap = hostsContent
|
||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(line => line.Trim())
|
||||
// skip full-line comments
|
||||
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"))
|
||||
// strip inline comments (truncate at '#')
|
||||
.Select(line =>
|
||||
{
|
||||
var index = line.IndexOf('#');
|
||||
return index >= 0 ? line.Substring(0, index).Trim() : line;
|
||||
})
|
||||
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#'))
|
||||
// ensure line still contains valid parts
|
||||
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
|
||||
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
.Select(line => line.Split([' ', '\t'], StringSplitOptions.RemoveEmptyEntries))
|
||||
.Where(parts => parts.Length >= 2)
|
||||
.GroupBy(parts => parts[0])
|
||||
.ToDictionary(
|
||||
|
|
|
|||
|
|
@ -88,14 +88,9 @@ public partial class CoreConfigSingboxService
|
|||
}
|
||||
}
|
||||
|
||||
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
|
||||
{
|
||||
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
||||
|
||||
foreach (var kvp in userHostsMap)
|
||||
{
|
||||
hostsDns.predefined[kvp.Key] = kvp.Value;
|
||||
}
|
||||
hostsDns.predefined[kvp.Key] = kvp.Value.Where(s => Utils.IsIpAddress(s)).ToList();
|
||||
}
|
||||
|
||||
foreach (var host in hostsDns.predefined)
|
||||
|
|
@ -115,7 +110,7 @@ public partial class CoreConfigSingboxService
|
|||
}
|
||||
|
||||
singboxConfig.dns ??= new Dns4Sbox();
|
||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
||||
singboxConfig.dns.servers ??= [];
|
||||
singboxConfig.dns.servers.Add(remoteDns);
|
||||
singboxConfig.dns.servers.Add(directDns);
|
||||
singboxConfig.dns.servers.Add(hostsDns);
|
||||
|
|
@ -191,13 +186,56 @@ public partial class CoreConfigSingboxService
|
|||
}
|
||||
});
|
||||
|
||||
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
|
||||
{
|
||||
var predefined = kvp.Value.First();
|
||||
if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode))
|
||||
{
|
||||
// xray syntactic sugar for predefined
|
||||
// etc. #0 -> NOERROR
|
||||
singboxConfig.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",
|
||||
rcode = "NOERROR",
|
||||
answer = [$"*. IN CNAME {predefined}."],
|
||||
};
|
||||
if (ParseV2Domain(kvp.Key, rule))
|
||||
{
|
||||
singboxConfig.dns.rules.Add(rule);
|
||||
}
|
||||
}
|
||||
|
||||
var (ech, _) = ParseEchParam(node?.EchConfigList);
|
||||
if (ech is not null)
|
||||
{
|
||||
var echDomain = ech.query_server_name ?? node?.Sni;
|
||||
singboxConfig.dns.rules.Add(new()
|
||||
{
|
||||
query_type = new List<int> { 64, 65 },
|
||||
query_type = [64, 65],
|
||||
server = Global.SingboxEchDNSTag,
|
||||
domain = echDomain is not null ? new List<string> { echDomain } : null,
|
||||
});
|
||||
|
|
@ -209,7 +247,7 @@ public partial class CoreConfigSingboxService
|
|||
{
|
||||
singboxConfig.dns.rules.Add(new()
|
||||
{
|
||||
query_type = new List<int> { 64, 65 },
|
||||
query_type = [64, 65],
|
||||
server = Global.SingboxEchDNSTag,
|
||||
domain = queryServerNames,
|
||||
});
|
||||
|
|
@ -220,9 +258,9 @@ public partial class CoreConfigSingboxService
|
|||
{
|
||||
singboxConfig.dns.rules.Add(new()
|
||||
{
|
||||
query_type = new List<int> { 64, 65 },
|
||||
query_type = [64, 65],
|
||||
action = "predefined",
|
||||
rcode = "NOTIMP"
|
||||
rcode = "NOERROR"
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -236,13 +274,14 @@ public partial class CoreConfigSingboxService
|
|||
type = "logical",
|
||||
mode = "and",
|
||||
rewrite_ttl = 1,
|
||||
rules = new List<Rule4Sbox>
|
||||
rules =
|
||||
[
|
||||
new()
|
||||
{
|
||||
new() {
|
||||
query_type = new List<int> { 1, 28 }, // A and AAAA
|
||||
query_type = [1, 28], // A and AAAA
|
||||
},
|
||||
fakeipFilterRule,
|
||||
}
|
||||
fakeipFilterRule
|
||||
]
|
||||
};
|
||||
|
||||
singboxConfig.dns.rules.Add(rule4Fake);
|
||||
|
|
@ -262,7 +301,7 @@ public partial class CoreConfigSingboxService
|
|||
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
||||
{
|
||||
var ipItems = simpleDNSItem.DirectExpectedIPs
|
||||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => s.Trim())
|
||||
.Where(s => !string.IsNullOrEmpty(s))
|
||||
.ToList();
|
||||
|
|
|
|||
|
|
@ -74,15 +74,9 @@ public partial class CoreConfigSingboxService
|
|||
var hostsDomains = new List<string>();
|
||||
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
if (dnsItem == null || !dnsItem.Enabled)
|
||||
{
|
||||
if (!simpleDnsItem.Hosts.IsNullOrEmpty())
|
||||
{
|
||||
var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts);
|
||||
foreach (var kvp in userHostsMap)
|
||||
{
|
||||
hostsDomains.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
hostsDomains.AddRange(userHostsMap.Select(kvp => kvp.Key));
|
||||
if (simpleDnsItem.UseSystemHosts == true)
|
||||
{
|
||||
var systemHostsMap = Utils.GetSystemHosts();
|
||||
|
|
|
|||
|
|
@ -333,15 +333,10 @@ public partial class CoreConfigV2rayService
|
|||
}
|
||||
}
|
||||
|
||||
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||
{
|
||||
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
||||
|
||||
foreach (var kvp in userHostsMap)
|
||||
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
|
||||
{
|
||||
dnsItem.hosts[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue