fix(sub): reduce SFA lag — local domain resolver + private direct
Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up. fix(sub): reduce SFA lag — local domain resolver + private direct Avoid DNS↔proxy loops by resolving outbound hostnames via local DNS, hijack DNS with port 53, route private IPs direct, drop auto_redirect, and use DoH remote DNS after the tunnel is up.
This commit is contained in:
1 parent
f7c6f8a0ae
commit
4e68f45c5d
2 files changed
+41
-26
No files matched your search
@@ -90,57 +90,67 @@ func buildSingboxSubscriptionDoc(outbounds []map[string]interface{}) map[string]
|
||||
"log": map[string]interface{}{
|
||||
"level": "warn",
|
||||
},
|
||||
// DNS is required for TUN profiles: without it, hijack-dns has nowhere to
|
||||
// resolve and browsers often load HTML but fail CSS/JS CDNs → broken pages.
|
||||
// https://sing-box.sagernet.org/configuration/dns/
|
||||
"dns": defaultSingboxDNS(),
|
||||
"inbounds": []map[string]interface{}{
|
||||
defaultSingboxTUNInbound(),
|
||||
},
|
||||
"outbounds": outbounds,
|
||||
// sing-box 1.11+: sniff / domain_strategy moved from inbound to route rule actions.
|
||||
// https://sing-box.sagernet.org/migration/#migrate-legacy-inbound-fields-to-rule-actions
|
||||
// Route: private traffic stays local; DNS is hijacked into the dns module;
|
||||
// everything else uses the proxy selector. Domain names of outbounds are
|
||||
// resolved via local DNS so we do not create a DNS↔proxy dependency loop
|
||||
// (a common cause of "works but extremely laggy" on SFA).
|
||||
"route": map[string]interface{}{
|
||||
"rules": []map[string]interface{}{
|
||||
{"action": "sniff"},
|
||||
{"protocol": "dns", "action": "hijack-dns"},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "or",
|
||||
"action": "hijack-dns",
|
||||
"rules": []map[string]interface{}{
|
||||
{"protocol": "dns"},
|
||||
{"port": 53},
|
||||
},
|
||||
},
|
||||
{"ip_is_private": true, "action": "route", "outbound": "direct"},
|
||||
},
|
||||
"final": "proxy",
|
||||
"auto_detect_interface": true,
|
||||
"default_domain_resolver": map[string]interface{}{
|
||||
"server": "local",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// defaultSingboxDNS sends queries through the proxy selector so domain
|
||||
// resolution matches the tunneled path (avoids split-horizon / poisoned local DNS).
|
||||
// defaultSingboxDNS: resolve outbound server names via local; general queries via
|
||||
// DoH through the proxy selector (after the outbound domain is known).
|
||||
func defaultSingboxDNS() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"servers": []map[string]interface{}{
|
||||
{
|
||||
"type": "udp",
|
||||
"tag": "remote",
|
||||
"server": "8.8.8.8",
|
||||
"server_port": 53,
|
||||
"detour": "proxy",
|
||||
},
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "local",
|
||||
"detour": "direct",
|
||||
},
|
||||
{
|
||||
"type": "https",
|
||||
"tag": "remote",
|
||||
"server": "8.8.8.8",
|
||||
"detour": "proxy",
|
||||
},
|
||||
},
|
||||
"rules": []map[string]interface{}{
|
||||
// Prefer local for private reverse lookups / plain local names.
|
||||
{"ip_is_private": true, "action": "route", "server": "local"},
|
||||
},
|
||||
"final": "remote",
|
||||
"strategy": "prefer_ipv4",
|
||||
}
|
||||
}
|
||||
|
||||
// defaultSingboxTUNInbound is a conservative TUN profile accepted by recent
|
||||
// sing-box builds used by SFI/SFM. Operators can still edit the profile on device.
|
||||
// defaultSingboxTUNInbound mirrors common SFA templates without stack/auto_redirect
|
||||
// (auto_redirect is Linux-oriented and often hurts mobile clients).
|
||||
func defaultSingboxTUNInbound() map[string]interface{} {
|
||||
// Tun fields aligned with common SFA-ready templates (e.g. sing-box_v1.14):
|
||||
// https://github.com/LongLights/sing-box_template_merge_sub-store
|
||||
// No "stack" — leave stack to the client default for better device compatibility.
|
||||
// No legacy inbound sniff (use route rule actions instead).
|
||||
return map[string]interface{}{
|
||||
"type": "tun",
|
||||
"tag": "tun-in",
|
||||
@@ -156,9 +166,8 @@ func defaultSingboxTUNInbound() map[string]interface{} {
|
||||
"192.0.0.0/24",
|
||||
"192.168.0.0/16",
|
||||
},
|
||||
"strict_route": true,
|
||||
"dns_mode": "hijack",
|
||||
"auto_redirect": true,
|
||||
"strict_route": true,
|
||||
"dns_mode": "hijack",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,14 +41,20 @@ func TestBuildSingboxSubscriptionDocIncludesTUN(t *testing.T) {
|
||||
if !ok || dns["final"] != "remote" {
|
||||
t.Fatalf("dns: %#v", parsed["dns"])
|
||||
}
|
||||
route, ok := parsed["route"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("missing route")
|
||||
}
|
||||
if _, ok := route["default_domain_resolver"]; !ok {
|
||||
t.Fatalf("missing default_domain_resolver: %#v", route)
|
||||
}
|
||||
if first["auto_route"] != true {
|
||||
t.Fatalf("auto_route: %#v", first["auto_route"])
|
||||
}
|
||||
if _, has := first["sniff"]; has {
|
||||
t.Fatalf("legacy inbound sniff must not be set (removed in sing-box 1.13): %#v", first)
|
||||
}
|
||||
route, ok := parsed["route"].(map[string]interface{})
|
||||
if !ok || route["final"] != "proxy" {
|
||||
if route["final"] != "proxy" {
|
||||
t.Fatalf("route.final: %#v", route)
|
||||
}
|
||||
rules, ok := route["rules"].([]interface{})
|
||||
|
||||
Reference in new issue
Block a user