feat(ruleset): auto-assign download_detour for remote rulesets & add watchdog self-heal for ruleset DNS stalls

This commit is contained in:
Dushnilin
2026-08-30 21:22:23 +03:00
parent dfb32722bf
commit 706b35328c
6 changed files with 190 additions and 19 deletions
+17 -13
View File
@@ -52,28 +52,32 @@ let proxy_port = "";
let proxy_interface = "";
if (warp_proxy_section != "") {
if (uci.get(CONFIG_NAME, warp_proxy_section)) {
let section_action = uci.get(CONFIG_NAME, warp_proxy_section, "action") || "";
let section_iface = uci.get(CONFIG_NAME, warp_proxy_section, "section_interface") ||
uci.get(CONFIG_NAME, warp_proxy_section, "interface") ||
uci.get(CONFIG_NAME, warp_proxy_section, "device") || "";
let section_port = uci.get(CONFIG_NAME, warp_proxy_section, "mixed_proxy_port") || "";
let clean_section = warp_proxy_section;
if (match(clean_section, /^netif:/)) {
clean_section = substr(clean_section, 6);
}
if (uci.get(CONFIG_NAME, clean_section)) {
let section_action = uci.get(CONFIG_NAME, clean_section, "action") || "";
let section_iface = uci.get(CONFIG_NAME, clean_section, "section_interface") ||
uci.get(CONFIG_NAME, clean_section, "interface") ||
uci.get(CONFIG_NAME, clean_section, "device") || "";
let section_port = uci.get(CONFIG_NAME, clean_section, "mixed_proxy_port") || "";
if (section_iface != "") {
proxy_interface = section_iface;
} else if (section_action == "interface") {
proxy_interface = warp_proxy_section;
proxy_interface = clean_section;
}
if (section_port != "" && match(section_port, /^[0-9]+$/) != null) {
proxy_port = section_port;
}
} else if (fs.stat("/sys/class/net/" + warp_proxy_section) != null) {
proxy_interface = warp_proxy_section;
} else if (uci.get("network", warp_proxy_section)) {
let net_dev = uci.get("network", warp_proxy_section, "device") ||
uci.get("network", warp_proxy_section, "ifname") ||
warp_proxy_section;
} else if (fs.stat("/sys/class/net/" + clean_section) != null) {
proxy_interface = clean_section;
} else if (uci.get("network", clean_section)) {
let net_dev = uci.get("network", clean_section, "device") ||
uci.get("network", clean_section, "ifname") ||
clean_section;
if (fs.stat("/sys/class/net/" + net_dev) != null) {
proxy_interface = net_dev;
}
+41 -2
View File
@@ -761,6 +761,44 @@ function heal_qos(ev) {
safe_reload_firewall();
}
function heal_uncached_rulesets_detour() {
let cfg = settings();
if (common.bool_option(cfg, "download_lists_via_proxy", false))
return false;
let has_rulesets = false;
let proxy_section_name = "";
uci_core.foreach(CONFIG_NAME, "section", function(s) {
if (!common.bool_option(s, "enabled", true)) return;
let act = common.option(s, "action", "");
if (act != "bypass" && act != "block" && act != "dns" && act != "") {
if (proxy_section_name == "")
proxy_section_name = s[".name"];
}
let lists = connections.community_lists(s);
let custom_rulesets = connections.rule_sets(s);
if (length(lists) > 0 || length(custom_rulesets) > 0)
has_rulesets = true;
});
if (has_rulesets && proxy_section_name != "") {
log_message("Watchdog: DNS stalled with remote rulesets on direct WAN; auto-enabling download_lists_via_proxy through " + proxy_section_name, "warn");
system("/sbin/uci set tachyon.settings.download_lists_via_proxy='1' >/dev/null 2>&1");
system("/sbin/uci set tachyon.settings.download_lists_via_proxy_section=" + shell_quote(proxy_section_name) + " >/dev/null 2>&1");
system("/sbin/uci commit tachyon >/dev/null 2>&1");
bg_system("/usr/bin/tachyon list_update");
ai_heal_report(
"ruleset_proxy_detour",
"DNS stalled during remote ruleset fetch on direct WAN",
"Включена загрузка списков через прокси-секцию " + proxy_section_name + " и запущено фоновое обновление",
"fixed"
);
return true;
}
return false;
}
// Threshold 3 is the original ai_heal_dns() streak: two isolated failures are
// noise, three in a row is a stall. The streak reset after acting prevents the
// in-flight restart from tripping the same threshold on the next tick.
@@ -771,12 +809,13 @@ function heal_dns_stall(ev) {
controller.reset_dns_streak();
let detour_healed = heal_uncached_rulesets_detour();
log_message("Watchdog: DNS stalled after 3 attempts, soft-reloading proxy runtime", "warn");
let incident = {
type: "dns",
description: "DNS resolution stalled on sing-box (port 53)",
resolution: "Soft-reloaded proxy runtime safely without breaking active TCP/RDP connections"
description: detour_healed ? "Зависание DNS из-за блокировки прямой загрузки списков правил" : "DNS resolution stalled on sing-box (port 53)",
resolution: detour_healed ? "Включена загрузка списков через прокси и выполнен мягкий перезапуск" : "Soft-reloaded proxy runtime safely without breaking active TCP/RDP connections"
};
// The restart is asynchronous, so `true` means only that it was launched.