fix(singbox): supply default /dns-query path and SNI for DoH/DoT IP addresses
- When users configure plain IP addresses (e.g. 8.8.8.8, 1.1.1.1, 77.88.8.8) with DoH (https in sing-box), sing-box requires a non-empty HTTP path (defaulting to /dns-query) and proper TLS SNI (server_name) matching the provider certificate. - Added default /dns-query path fallback in server_from_options(). - Added KNOWN_DNS_SERVER_NAMES mapping for Google, Cloudflare, Yandex, Quad9, AdGuard, Mullvad, Control D, AliDNS, OpenDNS, DNS.SB to provide correct TLS SNI when connecting to raw DNS IPs. - Added comprehensive DoH/DoT unit tests in tests/dns_failover.sh.
This commit is contained in:
@@ -152,6 +152,43 @@ function active_values(settings, override_state) {
|
||||
};
|
||||
}
|
||||
|
||||
const KNOWN_DNS_SERVER_NAMES = {
|
||||
"8.8.8.8": "dns.google",
|
||||
"8.8.4.4": "dns.google",
|
||||
"1.1.1.1": "cloudflare-dns.com",
|
||||
"1.0.0.1": "cloudflare-dns.com",
|
||||
"77.88.8.8": "common.dot.dns.yandex.net",
|
||||
"77.88.8.1": "common.dot.dns.yandex.net",
|
||||
"77.88.8.88": "safe.dot.dns.yandex.net",
|
||||
"77.88.8.2": "safe.dot.dns.yandex.net",
|
||||
"77.88.8.7": "family.dot.dns.yandex.net",
|
||||
"77.88.8.3": "family.dot.dns.yandex.net",
|
||||
"9.9.9.9": "dns.quad9.net",
|
||||
"149.112.112.112": "dns.quad9.net",
|
||||
"9.9.9.10": "dns10.quad9.net",
|
||||
"149.112.112.10": "dns10.quad9.net",
|
||||
"94.140.14.14": "dns.adguard-dns.com",
|
||||
"94.140.15.15": "dns.adguard-dns.com",
|
||||
"94.140.14.140": "unfiltered.adguard-dns.com",
|
||||
"94.140.14.141": "unfiltered.adguard-dns.com",
|
||||
"94.140.14.15": "family.adguard-dns.com",
|
||||
"94.140.15.16": "family.adguard-dns.com",
|
||||
"194.242.2.2": "dns.mullvad.net",
|
||||
"194.242.2.3": "dns.mullvad.net",
|
||||
"194.242.2.4": "dns.mullvad.net",
|
||||
"194.242.2.5": "dns.mullvad.net",
|
||||
"76.76.2.0": "freedns.controld.com",
|
||||
"76.76.10.0": "freedns.controld.com",
|
||||
"223.5.5.5": "dns.alidns.com",
|
||||
"223.6.6.6": "dns.alidns.com",
|
||||
"208.67.222.222": "doh.opendns.com",
|
||||
"208.67.220.220": "doh.opendns.com",
|
||||
"185.222.222.222": "doh.dns.sb",
|
||||
"45.11.45.11": "doh.dns.sb",
|
||||
"185.228.168.9": "doh.cleanbrowsing.org",
|
||||
"185.228.169.9": "doh.cleanbrowsing.org"
|
||||
};
|
||||
|
||||
function server_from_options(tag_name, dns_type, dns_server, detour) {
|
||||
let server = runtime_url.host(dns_server);
|
||||
let port = runtime_url.port(dns_server);
|
||||
@@ -169,18 +206,31 @@ function server_from_options(tag_name, dns_type, dns_server, detour) {
|
||||
else if (dns_type == "dot") {
|
||||
result.type = "tls";
|
||||
result.server_port = port != "" ? int(port) : 853;
|
||||
result.tls = { enabled: true };
|
||||
if (core_ip.valid_ip(server) && KNOWN_DNS_SERVER_NAMES[server])
|
||||
result.tls.server_name = KNOWN_DNS_SERVER_NAMES[server];
|
||||
else if (!core_ip.valid_ip(server))
|
||||
result.tls.server_name = server;
|
||||
}
|
||||
else if (dns_type == "doh") {
|
||||
result.type = "https";
|
||||
result.server_port = port != "" ? int(port) : 443;
|
||||
let path = runtime_url.path(dns_server);
|
||||
if (path != "")
|
||||
result.path = path;
|
||||
result.path = (path != "" && path != "/") ? path : "/dns-query";
|
||||
result.tls = { enabled: true };
|
||||
if (core_ip.valid_ip(server) && KNOWN_DNS_SERVER_NAMES[server])
|
||||
result.tls.server_name = KNOWN_DNS_SERVER_NAMES[server];
|
||||
else if (!core_ip.valid_ip(server))
|
||||
result.tls.server_name = server;
|
||||
}
|
||||
else if (dns_type == "doq") {
|
||||
result.type = "quic";
|
||||
result.server_port = port != "" ? int(port) : 784;
|
||||
result.tls = { enabled: true };
|
||||
if (core_ip.valid_ip(server) && KNOWN_DNS_SERVER_NAMES[server])
|
||||
result.tls.server_name = KNOWN_DNS_SERVER_NAMES[server];
|
||||
else if (!core_ip.valid_ip(server))
|
||||
result.tls.server_name = server;
|
||||
}
|
||||
else {
|
||||
return { unsupported: "unsupported dns_type " + dns_type };
|
||||
|
||||
@@ -160,4 +160,49 @@ verification="$(ucode -L "$TACHYON_LIB" "$FAILOVER" verification-plan-fixture "$
|
||||
printf '%s' "$verification" | grep -Eq '"main"[[:space:]]*:[[:space:]]*false' || fail "bootstrap-only switch must not require a dead main DNS to recover"
|
||||
printf '%s' "$verification" | grep -Eq '"bootstrap"[[:space:]]*:[[:space:]]*true' || fail "bootstrap-only switch must verify the selected bootstrap DNS"
|
||||
|
||||
# ─── DoH / DoT IP and domain server generation tests ──────────────────────────
|
||||
cat >"$WORK_DIR/test_doh_generation.uc" <<'UCODE'
|
||||
let dns = require("singbox.dns");
|
||||
|
||||
function assert(cond, msg) {
|
||||
if (!cond) {
|
||||
warn("FAIL: ", msg, "\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Plain IP 8.8.8.8 with DoH -> must produce path /dns-query and SNI dns.google
|
||||
let g_doh = dns.server_from_options("test-google", "doh", "8.8.8.8", "");
|
||||
assert(g_doh.type == "https", "google doh type is https");
|
||||
assert(g_doh.server == "8.8.8.8", "google doh server is 8.8.8.8");
|
||||
assert(g_doh.server_port == 443, "google doh port is 443");
|
||||
assert(g_doh.path == "/dns-query", "google doh path defaults to /dns-query");
|
||||
assert(g_doh.tls.enabled === true, "google doh tls enabled");
|
||||
assert(g_doh.tls.server_name == "dns.google", "google doh SNI is dns.google");
|
||||
|
||||
// 2. Plain IP 77.88.8.8 with DoH -> must produce SNI common.dot.dns.yandex.net
|
||||
let y_doh = dns.server_from_options("test-yandex", "doh", "77.88.8.8", "");
|
||||
assert(y_doh.path == "/dns-query", "yandex doh path is /dns-query");
|
||||
assert(y_doh.tls.server_name == "common.dot.dns.yandex.net", "yandex doh SNI is common.dot.dns.yandex.net");
|
||||
|
||||
// 3. Plain IP 1.1.1.1 with DoT -> must produce type tls and SNI cloudflare-dns.com
|
||||
let cf_dot = dns.server_from_options("test-cf", "dot", "1.1.1.1", "");
|
||||
assert(cf_dot.type == "tls", "cf dot type is tls");
|
||||
assert(cf_dot.server_port == 853, "cf dot port is 853");
|
||||
assert(cf_dot.tls.server_name == "cloudflare-dns.com", "cf dot SNI is cloudflare-dns.com");
|
||||
|
||||
// 4. Domain with custom path
|
||||
let custom_doh = dns.server_from_options("test-custom", "doh", "https://dns.example.com/custom-query", "proxy-out");
|
||||
assert(custom_doh.server == "dns.example.com", "custom doh server is dns.example.com");
|
||||
assert(custom_doh.path == "/custom-query", "custom doh path preserved");
|
||||
assert(custom_doh.detour == "proxy-out", "custom doh detour preserved");
|
||||
|
||||
print("OK\n");
|
||||
exit(0);
|
||||
UCODE
|
||||
|
||||
ucode -L "$TACHYON_LIB" "$WORK_DIR/test_doh_generation.uc" | grep -q "OK" ||
|
||||
fail "DoH server generation unit tests failed"
|
||||
|
||||
printf 'DNS failover checks passed\n'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user