Support sing-box 1.14 (#9736)

* Support sing-box 1.14

* Unified format

* Support parallel dns

* Add `optimistic DNS caching` support

* Try fix serial multi DNS

* Add HttpClient support
This commit is contained in:
DHR60
2026-09-02 03:54:06 +00:00
committed by GitHub
parent 1bf3d14771
commit f62eaab8c5
9 changed files with 337 additions and 151 deletions
@@ -378,50 +378,6 @@ public class CoreConfigSingboxServiceTests
await hasRouteRule.Should().BeTrue();
}
[Test]
public async Task GenerateClientConfigContent_DirectExpectedIPs_ShouldApplyGeoipAndCidrToDirectDnsRule()
{
var config = CoreConfigTestFactory.CreateConfigWithDirectExpectedIPs(
ECoreType.sing_box,
"192.168.0.0/16,geoip:cn");
CoreConfigTestFactory.BindAppManagerConfig(config);
var node = CoreConfigTestFactory.CreateSocksNode(ECoreType.sing_box, "n-main", "main");
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
{
RoutingItem = new RoutingItem
{
Id = "r-dns-direct-expected",
Remarks = "dns-direct-expected",
RuleSet = JsonUtils.Serialize(new List<RulesItem>
{
new()
{
Enabled = true,
RuleType = ERuleType.DNS,
OutboundTag = Global.DirectTag,
Domain = ["geosite:cn"],
}
}),
DomainStrategy = Global.AsIs,
DomainStrategy4Singbox = string.Empty,
}
};
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
var hasExpectedRule = cfg.dns.rules?.Any(r =>
r.server == Global.SingboxDirectDNSTag
&& r.ip_cidr?.Contains("192.168.0.0/16") == true
&& r.rule_set?.Contains("geosite-cn") == true
&& r.rule_set?.Contains("geoip-cn") == true) ?? false;
await hasExpectedRule.Should().BeTrue();
}
[Test]
public async Task GenerateClientConfigContent_BootstrapDNS_ShouldConfigurePureIPResolver()
{
@@ -544,14 +500,18 @@ public class CoreConfigSingboxServiceTests
Remarks = "dns-direct-variant",
RuleSet = JsonUtils.Serialize(new List<RulesItem>
{
new()
{
Enabled = true, RuleType = ERuleType.DNS, OutboundTag = Global.ProxyTag, Domain = ["geosite:google"],
},
new()
{
Enabled = true, RuleType = ERuleType.DNS, OutboundTag = Global.DirectTag, Domain = [domainTag],
}
},
}),
DomainStrategy = Global.AsIs,
DomainStrategy4Singbox = string.Empty,
}
},
};
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
@@ -559,11 +519,14 @@ public class CoreConfigSingboxServiceTests
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
var hasExpectedRule = cfg.dns.rules?.Any(r =>
r.server == Global.SingboxDirectDNSTag
&& r.ip_cidr?.Contains("192.168.0.0/16") == true
&& r.rule_set?.Contains(expectedRuleSetTag) == true
&& r.rule_set?.Contains("geoip-cn") == true) ?? false;
var hasExpectedRule = (cfg.dns.rules?.Any(r =>
r.ip_cidr?.Contains("192.168.0.0/16") == true
&& r.action == "respond"
&& r.rule_set?.Contains("geoip-cn") == true) ?? false)
&& (cfg.dns.rules?.Any(r =>
r.server == Global.SingboxDirectDNSTag
&& r.rule_set?.Contains(expectedRuleSetTag) == true
&& r.action == "evaluate") ?? false);
await hasExpectedRule.Should().BeTrue();
}
@@ -632,6 +595,60 @@ public class CoreConfigSingboxServiceTests
await cfg.dns.rules.Should().Contain(r => r.clash_mode == nameof(ERuleMode.Direct));
}
[Test]
[Arguments(true)]
[Arguments(false)]
public async Task GenerateClientConfigContent_MultiDnsServers_ShouldIncludeAllServers(bool parallelQuery)
{
var config =
CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
config.SimpleDNSItem.ParallelQuery = parallelQuery;
config.SimpleDNSItem.DirectDNS = "1.1.1.1,2.2.2.2";
config.SimpleDNSItem.RemoteDNS = "3.3.3.3,4.4.4.4";
CoreConfigTestFactory.BindAppManagerConfig(config);
var node = CoreConfigTestFactory.CreateSocksNode(ECoreType.sing_box, "n-main", "main");
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
{
RoutingItem = new RoutingItem
{
Id = "r-dns-direct-variant",
Remarks = "dns-direct-variant",
RuleSet = JsonUtils.Serialize(new List<RulesItem>
{
new()
{
Enabled = true,
RuleType = ERuleType.DNS,
OutboundTag = Global.ProxyTag,
Domain = ["geosite:google"],
},
new()
{
Enabled = true,
RuleType = ERuleType.DNS,
OutboundTag = Global.DirectTag,
Domain = ["geosite:cn"],
},
}),
DomainStrategy = Global.AsIs,
DomainStrategy4Singbox = string.Empty,
},
};
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
await cfg.dns.servers.Where(s => s.tag.StartsWith(Global.SingboxDirectDNSTagPrefix)).Should().HaveCount(2);
await cfg.dns.servers.Where(s => s.tag.StartsWith(Global.SingboxRemoteDNSTagPrefix)).Should().HaveCount(2);
if (parallelQuery)
{
await cfg.dns.rules.Should().Contain(r => r.race == true);
}
}
[Test]
public async Task GenerateClientConfigContent_Hysteria2Realm_ShouldEmitHttpsServerUrl()
{