Compare commits

...

3 commits

Author SHA1 Message Date
DHR60
cd04e170d3
Merge 780ba84393 into 7b31bcdd9f 2026-02-25 03:18:50 +00:00
DHR60
780ba84393 Fix DNS routing 2026-02-25 11:18:39 +08:00
DHR60
8b47a90c07 Fix routing 2026-02-25 10:18:47 +08:00
6 changed files with 135 additions and 69 deletions

View file

@ -49,6 +49,7 @@ public class Global
public const string DirectTag = "direct"; public const string DirectTag = "direct";
public const string BlockTag = "block"; public const string BlockTag = "block";
public const string DnsTag = "dns-module"; public const string DnsTag = "dns-module";
public const string DirectDnsTag = "direct-dns";
public const string BalancerTagSuffix = "-round"; public const string BalancerTagSuffix = "-round";
public const string StreamSecurity = "tls"; public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality"; public const string StreamSecurityReality = "reality";

View file

@ -220,12 +220,6 @@ public class DnsServer4Ray
public List<string>? domains { get; set; } public List<string>? domains { get; set; }
public bool? skipFallback { get; set; } public bool? skipFallback { get; set; }
public List<string>? expectedIPs { get; set; } public List<string>? expectedIPs { get; set; }
public List<string>? unexpectedIPs { get; set; }
public string? clientIp { get; set; }
public string? queryStrategy { get; set; }
public int? timeoutMs { get; set; }
public bool? disableCache { get; set; }
public bool? finalQuery { get; set; }
public string? tag { get; set; } public string? tag { get; set; }
} }

View file

@ -20,11 +20,14 @@ public partial class CoreConfigSingboxService
{ {
proxyOutboundList.AddRange(BuildGroupProxyOutbounds(baseTagName)); proxyOutboundList.AddRange(BuildGroupProxyOutbounds(baseTagName));
} }
if (withSelector)
{
var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList(); var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList();
if (proxyTags.Count > 1) if (proxyTags.Count > 1)
{ {
proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName)); proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName));
} }
}
return proxyOutboundList; return proxyOutboundList;
} }

View file

@ -239,6 +239,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
GenLog(); GenLog();
GenOutbounds(); GenOutbounds();
_coreConfig.routing.domainStrategy = Global.AsIs;
_coreConfig.routing.rules.Clear(); _coreConfig.routing.rules.Clear();
_coreConfig.inbounds.Clear(); _coreConfig.inbounds.Clear();
_coreConfig.inbounds.Add(new() _coreConfig.inbounds.Add(new()
@ -249,6 +250,8 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
protocol = EInboundProtocol.mixed.ToString(), protocol = EInboundProtocol.mixed.ToString(),
}); });
_coreConfig.routing.rules.Add(BuildFinalRule());
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true; ret.Success = true;
ret.Data = JsonUtils.Serialize(_coreConfig); ret.Data = JsonUtils.Serialize(_coreConfig);

View file

@ -70,18 +70,36 @@ public partial class CoreConfigV2rayService
dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null; dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null;
dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null; dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null;
if (_coreConfig.routing.domainStrategy == Global.IPIfNonMatch)
{
// DNS routing // DNS routing
dnsItem.tag = Global.DnsTag; var directDnsTags = dnsItem.servers
_coreConfig.routing.rules.Add(new RulesItem4Ray .Select(server =>
{
var tagNode = (server as JsonObject)?["tag"];
return tagNode is JsonValue value && value.TryGetValue<string>(out var tag) ? tag : null;
})
.Where(tag => tag is not null && tag.StartsWith(Global.DirectDnsTag, StringComparison.Ordinal))
.Select(tag => tag!)
.ToList();
if (directDnsTags.Count > 0)
{
_coreConfig.routing.rules.Add(new()
{ {
type = "field", type = "field",
inboundTag = new List<string> { Global.DnsTag }, inboundTag = directDnsTags,
outboundTag = Global.ProxyTag, outboundTag = Global.DirectTag,
}); });
} }
var finalRule = BuildFinalRule();
dnsItem.tag = Global.DnsTag;
_coreConfig.routing.rules.Add(new()
{
type = "field",
inboundTag = [Global.DnsTag],
outboundTag = finalRule.outboundTag,
balancerTag = finalRule.balancerTag,
});
_coreConfig.dns = dnsItem; _coreConfig.dns = dnsItem;
} }
catch (Exception ex) catch (Exception ex)
@ -93,45 +111,6 @@ public partial class CoreConfigV2rayService
private void FillDnsServers(Dns4Ray dnsItem) private void FillDnsServers(Dns4Ray dnsItem)
{ {
var simpleDNSItem = context.SimpleDnsItem; var simpleDNSItem = context.SimpleDnsItem;
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
{
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
.Select(addr => addr.Trim())
.Where(addr => !string.IsNullOrEmpty(addr))
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
.Distinct()
.ToList() ?? new List<string> { defaultAddress };
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
}
static object? CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
{
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
var domainFinal = dnsAddress;
int? portFinal = null;
if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = domain;
portFinal = port > 0 ? port : null;
}
else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = scheme + "://" + domain;
portFinal = port > 0 ? port : null;
}
var dnsServer = new DnsServer4Ray
{
address = domainFinal,
port = portFinal,
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(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.First()); var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.First());
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.First()); var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.First());
@ -252,22 +231,13 @@ public partial class CoreConfigV2rayService
dnsItem.servers ??= []; dnsItem.servers ??= [];
void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null) var directDnsTagIndex = 1;
{
if (domains.Count > 0)
{
foreach (var dnsAddress in dnsAddresses)
{
dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
}
}
}
AddDnsServers(remoteDNSAddress, proxyDomainList); AddDnsServers(remoteDNSAddress, proxyDomainList);
AddDnsServers(directDNSAddress, directDomainList); AddDnsServers(directDNSAddress, directDomainList, true);
AddDnsServers(remoteDNSAddress, proxyGeositeList); AddDnsServers(remoteDNSAddress, proxyGeositeList);
AddDnsServers(directDNSAddress, directGeositeList); AddDnsServers(directDNSAddress, directGeositeList, true);
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs); AddDnsServers(directDNSAddress, expectedDomainList, true, expectedIPs);
if (dnsServerDomains.Count > 0) if (dnsServerDomains.Count > 0)
{ {
AddDnsServers(bootstrapDNSAddress, dnsServerDomains); AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
@ -279,8 +249,78 @@ public partial class CoreConfigV2rayService
|| lastRule.Network == "tcp,udp" || lastRule.Network == "tcp,udp"
|| lastRule.Ip?.Contains("0.0.0.0/0") == true); || lastRule.Ip?.Contains("0.0.0.0/0") == true);
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; if (!useDirectDns)
dnsItem.servers.AddRange(defaultDnsServers); {
dnsItem.servers.AddRange(remoteDNSAddress);
}
else
{
foreach (var dns in directDNSAddress)
{
var dnsServer = CreateDnsServer(dns, []);
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
dnsServer.skipFallback = false;
dnsItem.servers.Add(JsonUtils.SerializeToNode(dnsServer,
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }));
}
}
return;
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
{
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
.Select(addr => addr.Trim())
.Where(addr => !string.IsNullOrEmpty(addr))
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
.Distinct()
.ToList() ?? [defaultAddress];
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
}
static DnsServer4Ray CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
{
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
var domainFinal = dnsAddress;
int? portFinal = null;
if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = domain;
portFinal = port > 0 ? port : null;
}
else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = scheme + "://" + domain;
portFinal = port > 0 ? port : null;
}
var dnsServer = new DnsServer4Ray
{
address = domainFinal,
port = portFinal,
skipFallback = true,
domains = domains.Count > 0 ? domains : null,
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
};
return dnsServer;
}
void AddDnsServers(List<string> dnsAddresses, List<string> domains, bool isDirectDns = false, List<string>? expectedIPs = null)
{
if (domains.Count <= 0)
{
return;
}
foreach (var dnsAddress in dnsAddresses)
{
var dnsServer = CreateDnsServer(dnsAddress, domains, expectedIPs);
if (isDirectDns)
{
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
}
var dnsServerNode = JsonUtils.SerializeToNode(dnsServer,
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
dnsItem.servers.Add(dnsServerNode);
}
}
} }
private void FillDnsHosts(Dns4Ray dnsItem) private void FillDnsHosts(Dns4Ray dnsItem)

View file

@ -45,6 +45,7 @@ public partial class CoreConfigV2rayService
rulesItem.outboundTag = null; rulesItem.outboundTag = null;
} }
} }
_coreConfig.routing.rules.Add(BuildFinalRule());
} }
} }
catch (Exception ex) catch (Exception ex)
@ -181,4 +182,28 @@ public partial class CoreConfigV2rayService
return tag; return tag;
} }
private RulesItem4Ray BuildFinalRule()
{
var finalRule = new RulesItem4Ray()
{
type = "field",
network = "tcp,udp",
outboundTag = Global.ProxyTag,
};
var balancer =
_coreConfig?.routing?.balancers?.FirstOrDefault(b => b.tag == Global.ProxyTag + Global.BalancerTagSuffix, null);
var domainStrategy = _coreConfig.routing?.domainStrategy ?? Global.AsIs;
if (balancer is not null)
{
finalRule.outboundTag = null;
finalRule.balancerTag = balancer.tag;
}
if (domainStrategy == Global.IPIfNonMatch)
{
finalRule.network = null;
finalRule.ip = ["0.0.0.0/0", "::/0"];
}
return finalRule;
}
} }