fix(singbox): prevent cold-start crash loop on missing community rulesets (#46)
- Bundle universal 17-byte empty SRS stub and add is_valid_srs_file/ensure_empty_srs_stub in rulesets.uc - Add prepare_community_rulesets in lifecycle.uc to pre-fetch via gh-proxy mirrors or initialize safe local stub before sing-box start - Support local community rulesets and stubs in generator_routes.uc - Add fallback to component proxy for list downloads in updates.uc and preserve cached stubs - Skip non-functional jsdelivr for release downloads and prioritize gh-proxy mirrors in core/url.uc - Add tests/cold_boot_community_stub.sh testing cold boot fallback behavior
This commit is contained in:
@@ -727,17 +727,37 @@ function ensure_custom_ruleset(config, reference) {
|
||||
tag_name = "builtin-" + reference + "-ruleset";
|
||||
kind = "domains";
|
||||
if (!ruleset_registered(config, tag_name)) {
|
||||
let rule_set = {
|
||||
type: "remote",
|
||||
tag: tag_name,
|
||||
format: "binary",
|
||||
url: runtime_rulesets.community_url(reference)
|
||||
};
|
||||
let detour = ctx.download_detour_tag(ctx.runtime_settings());
|
||||
if (is_valid_detour(config, detour))
|
||||
rule_set.download_detour = detour;
|
||||
rule_set.update_interval = remote_ruleset_update_interval();
|
||||
push(config.route.rule_set, rule_set);
|
||||
let folder = ctx.runtime_ruleset_folder || runtime_ruleset_folder;
|
||||
let tmp_srs = folder + "/community-" + reference + ".srs";
|
||||
let etc_srs = "/etc/tachyon/rulesets/community-" + reference + ".srs";
|
||||
let local_path = null;
|
||||
|
||||
if (runtime_rulesets.is_valid_srs_file(tmp_srs) || helpers.file_is_usable(tmp_srs, 16))
|
||||
local_path = tmp_srs;
|
||||
else if (runtime_rulesets.is_valid_srs_file(etc_srs) || helpers.file_is_usable(etc_srs, 16))
|
||||
local_path = etc_srs;
|
||||
|
||||
if (local_path != null) {
|
||||
push(config.route.rule_set, {
|
||||
type: "local",
|
||||
tag: tag_name,
|
||||
format: "binary",
|
||||
path: local_path
|
||||
});
|
||||
}
|
||||
else {
|
||||
let rule_set = {
|
||||
type: "remote",
|
||||
tag: tag_name,
|
||||
format: "binary",
|
||||
url: runtime_rulesets.community_url(reference)
|
||||
};
|
||||
let detour = ctx.download_detour_tag(ctx.runtime_settings());
|
||||
if (is_valid_detour(config, detour))
|
||||
rule_set.download_detour = detour;
|
||||
rule_set.update_interval = remote_ruleset_update_interval();
|
||||
push(config.route.rule_set, rule_set);
|
||||
}
|
||||
}
|
||||
return { tag: tag_name, kind };
|
||||
}
|
||||
@@ -795,9 +815,9 @@ function ensure_community_ruleset(config, section_name, community) {
|
||||
let etc_srs = "/etc/tachyon/rulesets/community-" + community + ".srs";
|
||||
let local_path = null;
|
||||
|
||||
if (helpers.file_is_usable(tmp_srs, 100))
|
||||
if (runtime_rulesets.is_valid_srs_file(tmp_srs) || helpers.file_is_usable(tmp_srs, 16))
|
||||
local_path = tmp_srs;
|
||||
else if (helpers.file_is_usable(etc_srs, 100))
|
||||
else if (runtime_rulesets.is_valid_srs_file(etc_srs) || helpers.file_is_usable(etc_srs, 16))
|
||||
local_path = etc_srs;
|
||||
|
||||
if (local_path != null) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/env ucode
|
||||
|
||||
let common = require("core.common");
|
||||
let fs = require("fs");
|
||||
|
||||
const EMPTY_SRS_B64 = "U1JTAXjaYgAEAAD//wABAAE=";
|
||||
const EMPTY_SRS_PATH = "/usr/share/tachyon/rulesets/empty.srs";
|
||||
|
||||
const SRS_MAIN_URL = "https://github.com/itdoginfo/allow-domains/releases/latest/download";
|
||||
const SRS_ADS_HAGEZI_PRO_URL = "https://github.com/zxc-rv/ad-filter/releases/latest/download/adlist.srs";
|
||||
@@ -126,15 +130,54 @@ function remote_format(reference) {
|
||||
return file_extension(reference) == "json" ? "source" : "binary";
|
||||
}
|
||||
|
||||
function is_valid_srs_file(path) {
|
||||
let p = as_string(path);
|
||||
let st = fs.stat(p);
|
||||
if (!st || st.size < 17)
|
||||
return false;
|
||||
let f = fs.open(p, "r");
|
||||
if (!f)
|
||||
return false;
|
||||
let magic = f.read(3);
|
||||
f.close();
|
||||
return magic == "SRS";
|
||||
}
|
||||
|
||||
function ensure_empty_srs_stub(target_path) {
|
||||
target_path = as_string(target_path);
|
||||
if (is_valid_srs_file(target_path))
|
||||
return true;
|
||||
|
||||
let slash = rindex(target_path, "/");
|
||||
if (slash > 0)
|
||||
common.ensure_dir(substr(target_path, 0, slash));
|
||||
|
||||
if (is_valid_srs_file(EMPTY_SRS_PATH)) {
|
||||
let content = fs.readfile(EMPTY_SRS_PATH);
|
||||
if (content && fs.writefile(target_path, content) != null)
|
||||
return true;
|
||||
}
|
||||
|
||||
let b = b64dec(EMPTY_SRS_B64);
|
||||
if (b && fs.writefile(target_path, b) != null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function module_exports() {
|
||||
return {
|
||||
EMPTY_SRS_PATH,
|
||||
EMPTY_SRS_B64,
|
||||
COMMUNITY_SERVICES,
|
||||
is_community,
|
||||
community_url,
|
||||
hash12,
|
||||
file_extension,
|
||||
kind_from_reference_hint,
|
||||
remote_format
|
||||
remote_format,
|
||||
is_valid_srs_file,
|
||||
ensure_empty_srs_stub
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,7 +194,11 @@ else if (mode == "kind-from-reference-hint")
|
||||
print(kind_from_reference_hint(ARGV[1]), "\n");
|
||||
else if (mode == "remote-format")
|
||||
print(remote_format(ARGV[1]), "\n");
|
||||
else if (mode == "is-valid-srs-file")
|
||||
exit(is_valid_srs_file(ARGV[1]) ? 0 : 1);
|
||||
else if (mode == "ensure-empty-srs-stub")
|
||||
exit(ensure_empty_srs_stub(ARGV[1]) ? 0 : 1);
|
||||
else {
|
||||
warn("Usage: singbox/rulesets.uc <file-extension|is-community|kind-from-reference-hint|remote-format> ...\n");
|
||||
warn("Usage: singbox/rulesets.uc <file-extension|is-community|kind-from-reference-hint|remote-format|is-valid-srs-file|ensure-empty-srs-stub> ...\n");
|
||||
exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user