63 lines
4.3 KiB
JavaScript
63 lines
4.3 KiB
JavaScript
// O adresa IP la care un serviciu PUBLIC al nostru nu are voie sa se conecteze in numele unui strain: loopback, retele private,
|
|
// link-local, CGNAT, adrese de documentatie si de test, multicast, rezervate, si formele IPv6 ale acelorasi (inclusiv IPv4 mapat
|
|
// si NAT64). UN singur loc, folosit de gateway (tintele webhook-urilor) si de scanerul de pregatire post-cuantica: doua copii ale
|
|
// aceleiasi liste diverg, iar cea din gateway nu stia de CGNAT (100.64/10), de IPv4 mapat in IPv6 privat si de `::`.
|
|
//
|
|
// Scris 2026-09-18, dupa ce scanerul PUBLIC, fara cheie, a fost prins (masurat pe serviciul viu) rezolvand o gazda straina la
|
|
// 127.0.0.1 si ::1 si INCERCAND conexiunea: oricine isi putea indrepta un nume spre o adresa interna si afla din raspuns daca
|
|
// portul 443 e deschis acolo si ce certificat poarta. Regula: orice nu e DOVEDIT public e privat (o adresa pe care nu o inteleg
|
|
// nu primeste conexiune).
|
|
const v4 = (ip) => { const p = ip.split('.'); if (p.length !== 4) return null; const n = p.map((x) => (/^\d{1,3}$/.test(x) ? Number(x) : NaN)); return n.every((x) => x >= 0 && x <= 255) ? n : null; };
|
|
|
|
function v4Privat([a, b, c]) {
|
|
return a === 0 || a === 10 || a === 127 || (a === 100 && b >= 64 && b <= 127) || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) ||
|
|
(a === 192 && b === 168) || (a === 192 && b === 0 && c === 0) || (a === 192 && b === 0 && c === 2) || (a === 192 && b === 88 && c === 99) ||
|
|
(a === 198 && (b === 18 || b === 19)) || (a === 198 && b === 51 && c === 100) || (a === 203 && b === 0 && c === 113) || a >= 224;
|
|
}
|
|
|
|
// IPv6 desfacut in 8 grupuri de 16 biti; null daca textul nu e o adresa IPv6
|
|
function v6Grupuri(ip) {
|
|
let t = ip.toLowerCase(); const zona = t.indexOf('%'); if (zona >= 0) t = t.slice(0, zona);
|
|
let coada4 = null;
|
|
const m = /^(.*:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(t);
|
|
if (m) { coada4 = v4(m[2]); if (!coada4) return null; t = m[1] + ((coada4[0] << 8) | coada4[1]).toString(16) + ':' + ((coada4[2] << 8) | coada4[3]).toString(16); }
|
|
if ((t.match(/::/g) || []).length > 1) return null;
|
|
const [st, dr] = t.includes('::') ? t.split('::') : [t, null];
|
|
const a = st ? st.split(':') : [], b = dr === null ? [] : (dr ? dr.split(':') : []);
|
|
if (dr === null ? a.length !== 8 : a.length + b.length > 7) return null;
|
|
const g = [...a, ...Array(8 - a.length - b.length).fill('0'), ...b];
|
|
if (!g.every((x) => /^[0-9a-f]{1,4}$/.test(x))) return null;
|
|
return g.map((x) => parseInt(x, 16));
|
|
}
|
|
|
|
export function ipPrivat(ip) {
|
|
const text = String(ip || '').trim();
|
|
const p4 = v4(text);
|
|
if (p4) return v4Privat(p4);
|
|
const g = v6Grupuri(text);
|
|
if (!g) return true; // nu e o adresa pe care o inteleg: nu primeste conexiune
|
|
const ultimele4 = [g[6] >> 8, g[6] & 255, g[7] >> 8, g[7] & 255];
|
|
if (g.slice(0, 5).every((x) => x === 0)) {
|
|
if (g[5] === 0xffff) return v4Privat(ultimele4); // ::ffff:a.b.c.d, IPv4 mapat: il judeca regula IPv4
|
|
if (g[5] === 0) return true; // ::, ::1 si vechiul ::a.b.c.d (retras): nimic public acolo
|
|
}
|
|
if (g[0] === 0x64 && g[1] === 0xff9b) return v4Privat(ultimele4); // NAT64 64:ff9b::/96 (si 64:ff9b:1::/48)
|
|
if (g[0] === 0x2002) return v4Privat([g[1] >> 8, g[1] & 255, g[2] >> 8, g[2] & 255]); // 6to4 poarta un IPv4 in el
|
|
if ((g[0] & 0xfe00) === 0xfc00) return true; // fc00::/7 unique local
|
|
if ((g[0] & 0xffc0) === 0xfe80) return true; // fe80::/10 link-local
|
|
if ((g[0] & 0xff00) === 0xff00) return true; // ff00::/8 multicast
|
|
if (g[0] === 0x2001 && g[1] === 0x0db8) return true; // documentatie
|
|
if (g[0] === 0x3fff && (g[1] & 0xf000) === 0) return true; // 3fff::/20, documentatie (RFC 9637); prins de proba, nu de mine
|
|
if (g[0] === 0x2001 && g[1] === 0) return true; // Teredo
|
|
if ((g[0] & 0xe000) !== 0x2000) return true; // in afara 2000::/3 (unicast global) nu e public
|
|
return false;
|
|
}
|
|
|
|
// Un `lookup` pentru tls.connect / https.request care intoarce NUMAI adresa deja verificata: intre verificare si conexiune
|
|
// numele nu mai e rezolvat a doua oara, deci un DNS care isi schimba raspunsul (rebinding) nu mai are ce sa mute.
|
|
export const lookupFixat = (adresa) => (gazda, optiuni, cb) => {
|
|
if (typeof optiuni === 'function') { cb = optiuni; optiuni = {}; }
|
|
if (optiuni && optiuni.all) cb(null, [{ address: adresa.address, family: adresa.family }]);
|
|
else cb(null, adresa.address, adresa.family);
|
|
};
|