51 lines
1.6 KiB
JavaScript
51 lines
1.6 KiB
JavaScript
function normalizeIp(ip) {
|
|
return String(ip || '').trim().replace(/^::ffff:/i, '').toLowerCase();
|
|
}
|
|
|
|
function isLoopbackIp(ip) {
|
|
const c = normalizeIp(ip);
|
|
return c === '' || c === '::1' || c === 'localhost' || /^127\./.test(c);
|
|
}
|
|
|
|
function ipv4ToInt(ip) {
|
|
const parts = String(ip).split('.');
|
|
if (parts.length !== 4) return null;
|
|
let n = 0;
|
|
for (const p of parts) {
|
|
if (!/^\d{1,3}$/.test(p)) return null;
|
|
const v = Number(p);
|
|
if (v < 0 || v > 255) return null;
|
|
n = (n << 8) + v;
|
|
}
|
|
return n >>> 0;
|
|
}
|
|
|
|
function matchIpRule(clientIp, rule) {
|
|
const client = normalizeIp(clientIp);
|
|
const r = String(rule || '').trim().toLowerCase();
|
|
if (!r) return false;
|
|
if (r === '*' || r === '0.0.0.0/0') return true;
|
|
if (r === client) return true;
|
|
const slash = r.indexOf('/');
|
|
if (slash > 0) {
|
|
const baseInt = ipv4ToInt(r.slice(0, slash));
|
|
const clientInt = ipv4ToInt(client);
|
|
const bits = Number(r.slice(slash + 1));
|
|
if (baseInt === null || clientInt === null || !Number.isInteger(bits) || bits < 0 || bits > 32) return false;
|
|
if (bits === 0) return true;
|
|
const mask = bits === 32 ? 0xffffffff : (~((1 << (32 - bits)) - 1)) >>> 0;
|
|
return (clientInt & mask) === (baseInt & mask);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function evaluateClientAllowed(clientIp, rules) {
|
|
const client = normalizeIp(clientIp);
|
|
if (isLoopbackIp(client)) return true;
|
|
const list = Array.isArray(rules) ? rules : [];
|
|
if (list.length === 0) return false;
|
|
return list.some((rule) => matchIpRule(client, rule));
|
|
}
|
|
|
|
module.exports = { normalizeIp, isLoopbackIp, ipv4ToInt, matchIpRule, evaluateClientAllowed };
|