Verified via node --check, eslint no-undef (2 known bugs remain: compare, webhooks), and smoke-test harness (28/28 match).
109 lines
4.1 KiB
JavaScript
109 lines
4.1 KiB
JavaScript
const localDb = require("../../local-db-client");
|
|
const { bgproutesResultCache, resultCacheGet, resultCacheSet } = require("../caches/response-caches");
|
|
|
|
// BGP endpoint (LOCAL DB): /api/bgp?asn=X (or prefix=X)
|
|
// Queries local PostgreSQL bgp_routes table — zero external API calls
|
|
async function handleBgp(req, res, url) {
|
|
const rawAsn = (url.searchParams.get("asn") || "").replace(/[^0-9]/g, "");
|
|
const prefix = url.searchParams.get("prefix") || "";
|
|
if (!rawAsn && !prefix) {
|
|
res.writeHead(400);
|
|
return res.end(JSON.stringify({ error: "Need asn or prefix parameter" }));
|
|
}
|
|
const cacheKey = rawAsn || prefix;
|
|
const cached = resultCacheGet(bgproutesResultCache, cacheKey);
|
|
if (cached !== undefined) {
|
|
res.writeHead(200, { "Content-Type": "application/json", "X-Cache": "HIT" });
|
|
return res.end(JSON.stringify(cached));
|
|
}
|
|
const start = Date.now();
|
|
try {
|
|
const result = {
|
|
meta: { timestamp: new Date().toISOString(), source: "local_bgp_db" },
|
|
bgp_status: null,
|
|
threat_intel: null,
|
|
};
|
|
|
|
// ---- BGP Status (local DB lookup) ----
|
|
if (prefix) {
|
|
// Prefix lookup: Get BGP status for this prefix
|
|
const bgpStatus = await localDb.getBgpStatus(prefix);
|
|
if (bgpStatus) {
|
|
result.bgp_status = {
|
|
prefix,
|
|
announced: bgpStatus.announced,
|
|
origin_asns: bgpStatus.origin_asns,
|
|
visibility_percent: bgpStatus.visibility_percent,
|
|
last_seen: bgpStatus.last_seen,
|
|
source: "local_bgp",
|
|
};
|
|
// Check for hijack (multiple origin ASNs). null means the check itself
|
|
// failed (DB error) -- distinct from a genuine single/no-origin result.
|
|
const hijackAsns = await localDb.checkBgpHijack(prefix);
|
|
if (hijackAsns === null) {
|
|
result.bgp_status.hijack_check_unavailable = true;
|
|
} else if (hijackAsns.length > 1) {
|
|
result.bgp_status.hijack_warning = {
|
|
detected: true,
|
|
origin_asns: hijackAsns,
|
|
message: `Multiple origin ASNs detected for ${prefix}`,
|
|
};
|
|
}
|
|
}
|
|
} else if (rawAsn) {
|
|
// ASN lookup: Get all announced prefixes for this ASN
|
|
const prefixes = await localDb.getAnnouncedPrefixes(rawAsn);
|
|
if (prefixes && prefixes.length > 0) {
|
|
result.bgp_status = {
|
|
asn: rawAsn,
|
|
announced_count: prefixes.length,
|
|
prefixes: prefixes.slice(0, 50).map((p) => ({
|
|
prefix: p.prefix,
|
|
origin_asn: p.origin_asn,
|
|
visibility_percent: p.visibility_percent,
|
|
last_seen: p.last_seen,
|
|
})),
|
|
source: "local_bgp",
|
|
};
|
|
} else {
|
|
result.bgp_status = {
|
|
asn: rawAsn,
|
|
announced: false,
|
|
announced_count: 0,
|
|
message: "No prefixes found for this ASN in local BGP table",
|
|
source: "local_bgp",
|
|
};
|
|
}
|
|
}
|
|
|
|
// ---- Threat Intelligence (local cache lookup) ----
|
|
// If we have an IP context, look up threat intel
|
|
if (prefix && prefix.includes(".")) {
|
|
// Extract IP from prefix (e.g., "1.1.1.0/24" → "1.1.1.0")
|
|
const ipAddr = prefix.split("/")[0];
|
|
const threat = await localDb.getThreatIntel(ipAddr);
|
|
if (threat) {
|
|
result.threat_intel = {
|
|
ip_address: threat.ip_address,
|
|
threat_level: threat.threat_level,
|
|
confidence_score: threat.confidence_score,
|
|
source: threat.source,
|
|
cached_at: threat.cached_at,
|
|
};
|
|
}
|
|
}
|
|
|
|
result.meta.duration_ms = Date.now() - start;
|
|
resultCacheSet(bgproutesResultCache, cacheKey, result);
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify(result, null, 2));
|
|
} catch (err) {
|
|
console.error("[/api/bgp] Error:", err.message);
|
|
res.writeHead(500);
|
|
return res.end(JSON.stringify({ error: "BGP query failed", message: err.message }));
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = { handleBgp };
|