Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
22 lines
1.4 KiB
JavaScript
22 lines
1.4 KiB
JavaScript
/** hal/cidr.mjs — IP 허용 목록(CIDR) 검사(§25 운영자 콘솔 네트워크 제한). IPv4·IPv6·::ffff: 로 감싼 IPv4 를 다룬다. */
|
|
import { isIP } from 'node:net';
|
|
|
|
function toBytes(ip) {
|
|
let s = String(ip || '').trim(); if (s.startsWith('::ffff:') && isIP(s.slice(7)) === 4) s = s.slice(7);
|
|
if (isIP(s) === 4) return { v: 4, b: s.split('.').map(Number) };
|
|
if (isIP(s) !== 6) return null;
|
|
const [head, tail] = s.split('::'); const h = head ? head.split(':') : []; const t = tail ? tail.split(':') : []; const parts = [...h, ...Array(8 - h.length - t.length).fill('0'), ...t];
|
|
const b = []; for (const p of parts) { const n = parseInt(p || '0', 16); b.push(n >> 8, n & 255); } return { v: 6, b };
|
|
}
|
|
/** ip 가 cidr 목록 중 하나에 속하면 true. 목록이 비면 false(허용 네트워크 없음). */
|
|
export function ipAllowed(ip, cidrs) {
|
|
const a = toBytes(ip); if (!a) return false;
|
|
for (const c of cidrs || []) {
|
|
const [net, lenS] = String(c).split('/'); const n = toBytes(net); if (!n || n.v !== a.v) continue;
|
|
const len = lenS === undefined ? (n.v === 4 ? 32 : 128) : Number(lenS); if (!(len >= 0 && len <= (n.v === 4 ? 32 : 128))) continue;
|
|
let ok = true; for (let i = 0; i < len; i++) { const byte = i >> 3, bit = 7 - (i & 7); if (((a.b[byte] >> bit) & 1) !== ((n.b[byte] >> bit) & 1)) { ok = false; break; } }
|
|
if (ok) return true;
|
|
}
|
|
return false;
|
|
}
|