@kanadi/core
Version:
Multi-Layer CAPTCHA Framework with customizable validators and challenge bundles
95 lines (79 loc) • 2.35 kB
text/typescript
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;
}
}