const { fetchHTML } = require("./services/http-helpers"); // Feature 24: bgp.he.net Integration. // NOTE: currently dead code in practice -- /api/lookup's only call site // (`timedFetch("bgp.he.net", ...)`) is hardcoded to `Promise.resolve(null)` // rather than actually invoking this. Moved as-is (not removed) since that's // a "disabled pending re-enable" state, not a provably-unreachable one -- // removing a whole scraping feature is a bigger decision than this // behavior-preserving extraction pass should make unilaterally. async function fetchBgpHeNet(asn) { try { const html = await fetchHTML("https://bgp.he.net/AS" + asn); if (!html) return null; const result = {}; const titleMatch = html.match(/([^<]+)<\/title>/i); if (titleMatch) result.title = titleMatch[1].trim(); const peerMatch = html.match(/BGP\s+Peers\s+Observed\s*\(all\)\s*:\s*(\d[\d,]*)/i) || html.match(/Observed\s+Peers[^<]*<[^>]*>\s*(\d+)/i); if (peerMatch) result.peer_count = parseInt(peerMatch[1].replace(/,/g, '')); const countryMatch = html.match(/Country[^<]*<[^>]*>[^<]*<[^>]*>\s*<[^>]*>([^<]+)/i); if (countryMatch) result.country = countryMatch[1].trim(); // Extract 2-letter country code from href="/country/XX" const ccMatch = html.match(/href="\/country\/([A-Z]{2})"/i); if (ccMatch) result.country_code = ccMatch[1].toUpperCase(); // Extract clean AS name from title: "AS12345 Some Name - bgp.he.net" → "Some Name" if (titleMatch) { const rawTitle = titleMatch[1].trim(); const nameFromTitle = rawTitle.replace(/^AS\d+\s+/i, '').replace(/\s+-\s+bgp\.he\.net.*$/i, '').trim(); if (nameFromTitle && !nameFromTitle.toLowerCase().includes('bgp.he.net')) { result.name_from_title = nameFromTitle; } } const lgMatch = html.match(/Looking\s+Glass[^<]*<[^>]*href="([^"]+)"/i); if (lgMatch) result.looking_glass = lgMatch[1]; const descMatch = html.match(/AS\s+Name[^<]*<[^>]*>[^<]*<[^>]*>([^<]+)/i); if (descMatch) result.description = descMatch[1].trim(); const irrMatch = html.match(/IRR\s+Record[^<]*<[^>]*>[^<]*<[^>]*>([^<]+)/i); if (irrMatch) result.irr_record = irrMatch[1].trim(); // bgp.he.net format: "Prefixes Originated (v4): 147<br/>" or "Prefixes v4 ... <td>147" const v4Match = html.match(/Prefixes\s+Originated\s*\(v4\)\s*:\s*(\d[\d,]*)/i) || html.match(/Prefixes\s+v4[^<]*<[^>]*>\s*(\d+)/i); if (v4Match) result.prefixes_v4 = parseInt(v4Match[1].replace(/,/g, '')); const v6Match = html.match(/Prefixes\s+Originated\s*\(v6\)\s*:\s*(\d[\d,]*)/i) || html.match(/Prefixes\s+v6[^<]*<[^>]*>\s*(\d+)/i); if (v6Match) result.prefixes_v6 = parseInt(v6Match[1].replace(/,/g, '')); const allMatch = html.match(/Prefixes\s+Originated\s*\(all\)\s*:\s*(\d[\d,]*)/i); if (allMatch) result.prefixes_all = parseInt(allMatch[1].replace(/,/g, '')); result.source_url = "https://bgp.he.net/AS" + asn; return result; } catch (_e) { return null; } } module.exports = { fetchBgpHeNet };