fix: RIR+Country empty (RIPE Stat .location field), RDAP parallel race (v0.6.7)
This commit is contained in:
parent
9be247410c
commit
9012d2931f
60
server.js
60
server.js
@ -2089,7 +2089,7 @@ const server = http.createServer(async (req, res) => {
|
||||
JSON.stringify({
|
||||
status,
|
||||
service: "PeerCortex",
|
||||
version: "0.6.6",
|
||||
version: "0.6.7",
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime_seconds: Math.floor(process.uptime()),
|
||||
memory_mb: Math.round(mem.heapUsed / 1024 / 1024),
|
||||
@ -3138,10 +3138,13 @@ const server = http.createServer(async (req, res) => {
|
||||
}
|
||||
|
||||
const pocQuery = netId ? "/poc?net_id=" + netId + "&limit=25" : null;
|
||||
// RDAP: try all 5 RIRs in parallel, take the first valid response (fast race)
|
||||
const rdapForReg = [
|
||||
"https://rdap.db.ripe.net/autnum/" + asn,
|
||||
"https://rdap.apnic.net/autnum/" + asn,
|
||||
"https://rdap.arin.net/registry/autnum/" + asn,
|
||||
"https://rdap.apnic.net/autnum/" + asn,
|
||||
"https://rdap.lacnic.net/rdap/autnum/" + asn,
|
||||
"https://rdap.afrinic.net/rdap/autnum/" + asn,
|
||||
];
|
||||
|
||||
const promises = [
|
||||
@ -3156,15 +3159,15 @@ const server = http.createServer(async (req, res) => {
|
||||
timedFetch("PeeringDB IXLan", cachedIxlan ? Promise.resolve(cachedIxlan) : fetchPeeringDBWithRetry(ixQuery)),
|
||||
timedFetch("PeeringDB Facilities", cachedFac ? Promise.resolve(cachedFac) : (netId ? fetchPeeringDBWithRetry("/netfac?net_id=" + netId + "&limit=1000") : Promise.resolve(null))),
|
||||
timedFetch("PeeringDB Contacts", pocQuery ? fetchPeeringDB(pocQuery).catch(() => null) : Promise.resolve(null)),
|
||||
timedFetch("RDAP Registration", (async () => {
|
||||
for (const url of rdapForReg) {
|
||||
try {
|
||||
const d = await fetchJSON(url, { timeout: 5000 });
|
||||
if (d && !d.errorCode && d.handle) return d;
|
||||
} catch(e) {}
|
||||
}
|
||||
return null;
|
||||
})()),
|
||||
timedFetch("RDAP Registration", Promise.race([
|
||||
// All 5 RIR RDAP endpoints in parallel — first valid wins
|
||||
...rdapForReg.map(url =>
|
||||
fetchJSON(url, { timeout: 4000 })
|
||||
.then(d => (d && !d.errorCode && d.handle) ? d : new Promise(() => {}))
|
||||
.catch(() => new Promise(() => {}))
|
||||
),
|
||||
new Promise(resolve => setTimeout(() => resolve(null), 5000)),
|
||||
])),
|
||||
];
|
||||
const [prefixData, neighbourData, overviewData, rirData, atlasProbeData, bgpHeData, visibilityData, prefixSizeData, ixlanData, facData, pocData, rdapData] = await Promise.all(promises);
|
||||
|
||||
@ -3308,14 +3311,45 @@ const server = http.createServer(async (req, res) => {
|
||||
|
||||
let rir = "";
|
||||
let country = "";
|
||||
// RIPE Stat rir-stats-country uses 'location' field (not 'country' or 'rir')
|
||||
if (Array.isArray(rirEntries) && rirEntries.length > 0) {
|
||||
country = rirEntries[0]?.location || rirEntries[0]?.country || "";
|
||||
rir = rirEntries[0]?.rir || "";
|
||||
country = rirEntries[0]?.country || "";
|
||||
}
|
||||
if (!rir && rirData?.data) {
|
||||
const rirField = rirData.data.rirs || [];
|
||||
if (rirField.length > 0) rir = rirField[0]?.rir || "";
|
||||
}
|
||||
// Derive RIR from rdapData.port43 (e.g. "whois.ripe.net" → "RIPE")
|
||||
if (!rir && rdapData && rdapData.port43) {
|
||||
const p43 = (rdapData.port43 || "").toLowerCase();
|
||||
if (p43.includes("ripe")) rir = "RIPE";
|
||||
else if (p43.includes("arin")) rir = "ARIN";
|
||||
else if (p43.includes("apnic")) rir = "APNIC";
|
||||
else if (p43.includes("lacnic")) rir = "LACNIC";
|
||||
else if (p43.includes("afrinic")) rir = "AFRINIC";
|
||||
}
|
||||
// Also derive RIR from RDAP links (URL of the RDAP endpoint that responded)
|
||||
if (!rir && rdapData && rdapData.links) {
|
||||
const selfLink = (rdapData.links.find(l => l.rel === "self") || {}).href || "";
|
||||
if (selfLink.includes("ripe")) rir = "RIPE";
|
||||
else if (selfLink.includes("arin")) rir = "ARIN";
|
||||
else if (selfLink.includes("apnic")) rir = "APNIC";
|
||||
else if (selfLink.includes("lacnic")) rir = "LACNIC";
|
||||
else if (selfLink.includes("afrinic")) rir = "AFRINIC";
|
||||
}
|
||||
// Last resort: derive RIR from country code (common assignments)
|
||||
if (!rir && country) {
|
||||
const ARIN_CC = new Set(["US","CA","AI","AG","BS","BB","BZ","VG","KY","DM","DO","GD","GP","HT","JM","MQ","MS","PR","KN","LC","VC","TT","TC","VI","UM"]);
|
||||
const APNIC_CC = new Set(["AU","NZ","JP","CN","KR","IN","HK","SG","TW","VN","TH","ID","MY","PK","BD","LK","NP","PH","AF","KH","LA","MM","MN","BT","BN","FJ","PG","WS","TO","VU","SB","KI","NR","TV","FM","MH","PW","CK","NU","TK","WF","PF","NC","GU","MP","AS","CC","CX","HM","NF"]);
|
||||
const LACNIC_CC = new Set(["BR","AR","MX","CO","CL","PE","VE","EC","UY","BO","PY","CU","GT","HN","SV","NI","CR","PA","GY","SR","GF","AW","CW","SX","BQ","AN"]);
|
||||
const AFRINIC_CC = new Set(["ZA","NG","KE","EG","GH","TZ","UG","MA","CI","SN","ZM","ZW","AO","MZ","CM","ET","SD","MG","DZ","TN","LY","RW","NA","BW","MW","ML","BF","NE","GN","TD","SO","LS","SZ","ER","DJ","GM","SL","LR","TG","BJ","GW","CF","CG","CD","GQ","ST","KM","MR","SC","MU","RE","CV","BU","SS","EH"]);
|
||||
if (ARIN_CC.has(country)) rir = "ARIN";
|
||||
else if (APNIC_CC.has(country)) rir = "APNIC";
|
||||
else if (LACNIC_CC.has(country)) rir = "LACNIC";
|
||||
else if (AFRINIC_CC.has(country)) rir = "AFRINIC";
|
||||
else rir = "RIPE"; // Europe + rest = RIPE NCC
|
||||
}
|
||||
|
||||
const duration = Date.now() - start;
|
||||
|
||||
@ -3501,7 +3535,7 @@ const server = http.createServer(async (req, res) => {
|
||||
const result = {
|
||||
meta: {
|
||||
service: "PeerCortex",
|
||||
version: "0.6.6",
|
||||
version: "0.6.7",
|
||||
query: "AS" + asn,
|
||||
duration_ms: duration,
|
||||
sources: ["PeeringDB", "RIPE Stat", "bgp.he.net", "Cloudflare RPKI", "RIPE RPKI Validator", "Route Views"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user