UNPKG

@kanadi/core

Version:

Multi-Layer CAPTCHA Framework with customizable validators and challenge bundles

95 lines (79 loc) 2.35 kB
import { createLogger } from "../utils/logger"; const logger = createLogger("IPUtils"); export function parseCIDR(cidr: string): { network: string; prefix: number; start: bigint; end: bigint } { const [ip, prefixStr] = cidr.split("/"); const prefix = parseInt(prefixStr); if (!ip || isNaN(prefix) || prefix < 0 || prefix > 32) { throw new Error(`Invalid CIDR notation: ${cidr}`); } const ipNum = ipToNumber(ip); const mask = BigInt(0xFFFFFFFF) << BigInt(32 - prefix); const network = ipNum & mask; const broadcast = network | (~mask & BigInt(0xFFFFFFFF)); return { network: numberToIP(network), prefix, start: network, end: broadcast, }; } export function ipToNumber(ip: string): bigint { const parts = ip.split(".").map(Number); if (parts.length !== 4 || parts.some(p => p < 0 || p > 255)) { throw new Error(`Invalid IP address: ${ip}`); } return BigInt( (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3] ) >>> BigInt(0); } export function numberToIP(num: bigint): string { const n = Number(num); return [ (n >>> 24) & 0xFF, (n >>> 16) & 0xFF, (n >>> 8) & 0xFF, n & 0xFF ].join("."); } export function isIPInCIDR(ip: string, cidr: string): boolean { try { const ipNum = ipToNumber(ip); const { start, end } = parseCIDR(cidr); return ipNum >= start && ipNum <= end; } catch { return false; } } export interface ASNPrefix { prefix: string; ip: string; cidr: number; name: string; description: string; } export async function fetchASNPrefixes(asn: string): Promise<ASNPrefix[]> { const asnNumber = asn.replace(/^AS/i, ""); try { const response = await fetch(`https://api.bgpview.io/asn/${asnNumber}/prefixes`); if (!response.ok) { throw new Error(`BGPView API error: ${response.status}`); } const data = await response.json(); const prefixes: ASNPrefix[] = []; if (data.data?.ipv4_prefixes) { for (const prefix of data.data.ipv4_prefixes) { prefixes.push({ prefix: prefix.prefix, ip: prefix.ip || prefix.prefix.split("/")[0], cidr: parseInt(prefix.prefix.split("/")[1]), name: prefix.name || `AS${asnNumber}`, description: prefix.description || `ASN ${asnNumber} prefix`, }); } } return prefixes; } catch (error) { logger.error(`Failed to fetch ASN ${asnNumber} prefixes`, error); throw error; } }