mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-03 11:33:56 +08:00
🎈 Sync 2026-10-04 14:38:18
This commit is contained in:
1 parent
0e6611f767
commit
aba12667c5
88 files changed
+4600
-1458
No files matched your search
@@ -300,116 +300,6 @@ export function ruleSetFormatFromPath(path) {
|
||||
return null;
|
||||
};
|
||||
|
||||
/* --- the "do not block startup on the first download" fallback ---------- *
|
||||
*
|
||||
* A remote rule-set with no `initial_path` is fetched during
|
||||
* initialization, BEFORE the inbounds bind. On a cold cache that means a
|
||||
* fresh install cannot start until raw.githubusercontent.com answers - and if
|
||||
* it does not, the whole instance dies with a FATAL and the health gate turns
|
||||
* that into a rollback or a released intercept layer. The node has to be up
|
||||
* too, because these downloads go through http_clients, so a working
|
||||
* configuration can be kept off the air by an unreachable CDN.
|
||||
*
|
||||
* `initial_path` is the way out: sing-box reads the file first, starts
|
||||
* immediately, and refreshes from the URL in the background. A download that
|
||||
* keeps failing then becomes an ERROR line in the log instead of a failed
|
||||
* start - and the health gate does not look at rule-sets at all, so a
|
||||
* degraded rule-set is promoted like any other healthy start.
|
||||
*
|
||||
* What the file holds is an EMPTY rule-set, and that is the whole trade: a
|
||||
* rule-set with no rules matches nothing, so its traffic falls through to
|
||||
* `final`. That changes routing, so this is opt-in (config.ruleset_safe_start,
|
||||
* default off) and never the default.
|
||||
*
|
||||
* The format has to match the declared one. A binary rule-set fed a source
|
||||
* JSON is not read at all - sing-box ignores the mismatch and blocks exactly
|
||||
* as it does with no initial_path - so the decision of whether a fallback is
|
||||
* even possible is made once, here, from the same inputs on both sides. */
|
||||
|
||||
/* Where the generated fallbacks live. Inside the rule-set archive, so
|
||||
* validateRuleSetPath() admits it and the jail's read-only mount of HP_DIR
|
||||
* already exposes it to the sing-box user. */
|
||||
export const RULESET_INITIAL_DIR = HP_DIR + '/ruleset/initial';
|
||||
|
||||
/* The two files the package ships for this. empty.source.json is what
|
||||
* `sing-box rule-set compile` consumes (24 bytes, readable, and the form
|
||||
* every version agrees on); empty.srs is the compiled result of THAT source
|
||||
* on 1.14.2, kept as the last resort for a router where the compile cannot
|
||||
* run.
|
||||
*
|
||||
* The .srs is committed rather than built at package time on purpose: it is a
|
||||
* 14-byte binary, and having it in the tree means `hexdump -C` on a router
|
||||
* can confirm byte for byte that the shipped constant is the documented one.
|
||||
* It is deliberately NOT a conffile - it is ours, not the user's, and an
|
||||
* upgrade may replace it freely.
|
||||
*
|
||||
* Compatibility note, since a hard-coded binary artifact invites the question:
|
||||
* sing-box only rejects an SRS whose version is NEWER than its own, so a
|
||||
* version-2 file stays readable. The compile path exists anyway, because a
|
||||
* file produced by the kernel that is running it cannot drift from it. */
|
||||
export const RULESET_EMPTY_SOURCE = RULESET_INITIAL_DIR + '/empty.source.json';
|
||||
export const RULESET_EMPTY_BINARY = RULESET_INITIAL_DIR + '/empty.srs';
|
||||
|
||||
/* A tag becomes a file name, so it has to be one. UCI section names are
|
||||
* already restricted, but a rule_set `tag` is a sing-box identifier that UCI
|
||||
* does not fully own, and this value is about to be concatenated into a path
|
||||
* the generator writes as root. Anything outside this set is refused, and
|
||||
* the caller skips the fallback rather than sanitising it into a name that
|
||||
* could collide with another rule-set's. */
|
||||
export function isSafeRuleSetTag(tag) {
|
||||
if (!tag || type(tag) !== 'string')
|
||||
return false;
|
||||
|
||||
return match(tag, /^[A-Za-z0-9][A-Za-z0-9._-]*$/) != null;
|
||||
};
|
||||
|
||||
/* The extension a format is stored under. Null for anything that is not one
|
||||
* of the two formats, which is the caller's signal that no fallback is
|
||||
* possible rather than something to guess at. */
|
||||
export function ruleSetFormatExtension(format) {
|
||||
if (format === 'binary')
|
||||
return 'srs';
|
||||
|
||||
if (format === 'source')
|
||||
return 'json';
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/* ruleSetInitialFallback(tags, declared, url) -> the initial_path to emit, or
|
||||
* null when no fallback is possible.
|
||||
*
|
||||
* One function, called from both sides on purpose. The CLI asks it to
|
||||
* decide which files to create and the generator asks it where to point
|
||||
* `initial_path`; if they each worked it out separately they could disagree
|
||||
* about the format, and a mismatch is the failure this whole mechanism exists
|
||||
* to avoid - sing-box ignores a wrong-format initial file and blocks startup
|
||||
* exactly as if there were none.
|
||||
*
|
||||
* `declared` is the rule-set's own `format`, which for a rule-set with no
|
||||
* initial file is also the format the generator emits (nothing to probe, so
|
||||
* nothing is corrected). With no declared format the URL's extension is the
|
||||
* only remaining evidence; with neither, the answer is null and the rule-set
|
||||
* keeps today's behaviour rather than being handed a file of the wrong shape.
|
||||
*
|
||||
* More than one tag gets a `{tag}` placeholder, because that is what sing-box
|
||||
* substitutes and it requires every tag's file to exist - P3 on 1.14.2: a
|
||||
* literal "{tag}.srs" on disk makes it try to download and block. */
|
||||
export function ruleSetInitialFallback(tags, declared, url) {
|
||||
const format = declared || ruleSetFormatFromPath(url);
|
||||
const ext = ruleSetFormatExtension(format);
|
||||
|
||||
if (!ext)
|
||||
return null;
|
||||
|
||||
for (let tag in (tags || []))
|
||||
if (!isSafeRuleSetTag(tag))
|
||||
return null;
|
||||
|
||||
const stem = (length(tags || []) > 1) ? '{tag}' : tags[0];
|
||||
|
||||
return RULESET_INITIAL_DIR + '/' + stem + '.' + ext;
|
||||
};
|
||||
|
||||
/* probeRuleSetFile(path) -> 'binary' | 'source' | null.
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user