atlas-probes.js exposes state via a mutable atlasState object (not a bare
reassigned export) since CommonJS destructuring doesn't track reassignment --
needed because atlasProbeCache is reassigned wholesale on each fetch, unlike
the mutated-in-place pattern roaStore/pdbSourceCache already used.
manrs.js: rewrote the ASN-extraction loop from `while ((m = re.exec(str)))`
to `for (const m of str.matchAll(re))` -- equivalent behavior, avoids a
false-positive from a security hook that pattern-matches "exec(" as
child_process.exec (this is RegExp.exec, unrelated).
Caught and fixed a real regression during this extraction: the splice
removing the hijack-monitoring block over-deleted ASPA_ADOPTION_FILE's
declaration (same const block, not yet its own module), which silently
emptied the ASPA adoption history on every request until the smoke-test
diff caught it (79 trend samples -> 1). Re-added the constant; verified
via a second smoke-test run (28/28 match).
server.js: 5907 -> 5625 lines.
65 lines
2.4 KiB
JavaScript
65 lines
2.4 KiB
JavaScript
const https = require("https");
|
|
const { UA } = require("../data/constants");
|
|
|
|
// MANRS Participants Cache (scraped from public HTML page, 24h TTL)
|
|
let manrsAsnSet = null; // Set<string> of member ASNs
|
|
let manrsLastFetch = 0;
|
|
let manrsFetching = false;
|
|
const MANRS_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
|
|
|
async function ensureManrsCache() {
|
|
const now = Date.now();
|
|
if (manrsAsnSet && (now - manrsLastFetch) < MANRS_CACHE_TTL) return;
|
|
if (manrsFetching) {
|
|
// Wait up to 8s for in-progress fetch
|
|
for (let i = 0; i < 80; i++) {
|
|
await new Promise(r => setTimeout(r, 100));
|
|
if (manrsAsnSet) return;
|
|
}
|
|
return;
|
|
}
|
|
manrsFetching = true;
|
|
try {
|
|
const html = await new Promise((resolve, reject) => {
|
|
const opts = { hostname: "www.manrs.org", path: "/netops/participants/", method: "GET", timeout: 15000,
|
|
headers: { "User-Agent": UA, "Accept": "text/html" } };
|
|
const req = https.request(opts, res => {
|
|
let body = "";
|
|
res.on("data", d => { body += d; });
|
|
res.on("end", () => resolve(body));
|
|
});
|
|
req.on("error", reject);
|
|
req.on("timeout", () => { req.destroy(); reject(new Error("MANRS fetch timeout")); });
|
|
req.end();
|
|
});
|
|
// Extract ASNs from <td class="asns">267490</td> — may contain multiple space-separated ASNs
|
|
const set = new Set();
|
|
const re = /<td[^>]*class="asns"[^>]*>([\d\s,]+)<\/td>/gi;
|
|
for (const m of html.matchAll(re)) {
|
|
m[1].split(/[\s,]+/).forEach(a => { const n = a.trim(); if (n) set.add(n); });
|
|
}
|
|
if (set.size > 0) {
|
|
manrsAsnSet = set;
|
|
manrsLastFetch = Date.now();
|
|
console.log("[MANRS] Loaded " + set.size + " participant ASNs from manrs.org");
|
|
}
|
|
} catch (e) {
|
|
console.warn("[MANRS] Failed to fetch participants:", e.message);
|
|
} finally {
|
|
manrsFetching = false;
|
|
}
|
|
}
|
|
|
|
function checkManrsMembership(asn) {
|
|
if (!manrsAsnSet) return { status: "info", participant: "unknown", message: "MANRS data not yet loaded", note: "https://www.manrs.org/netops/participants/" };
|
|
const isMember = manrsAsnSet.has(String(asn));
|
|
return {
|
|
status: isMember ? "pass" : "fail",
|
|
participant: isMember,
|
|
member_count: manrsAsnSet.size,
|
|
note: isMember ? "Confirmed MANRS Network Operator participant" : "Not listed as MANRS participant — https://www.manrs.org/beamanrs/",
|
|
};
|
|
}
|
|
|
|
module.exports = { ensureManrsCache, checkManrsMembership };
|