Skip to main content
Module

x/cidr2range/convert.ts

Convert utilities for CIDR and IP address
Latest
File
import { isIP, isIPv4, isIPv6 } from "./deps.ts";
/** cidr2range converts CIDR to range of IP address. */export function cidr2range(cidr: string): string[] { const addressParts = cidr.split("/"); const ipAddress = addressParts[0]; if (addressParts.length > 2) { throw new Error(`${cidr} contains more than one slash`); } if (!isIP(ipAddress)) { throw new Error(`${cidr} is invalid cidr format`); } if (isIPv4(ipAddress)) { return cidr2ipv4range(cidr, addressParts); } if (isIPv6(ipAddress)) { return cidr2ipv6range(cidr, addressParts); }
return [];}
function cidr2ipv4range(cidr: string, addressParts: string[]): string[] { const netmask = addressParts.length == 2 ? Number(addressParts[1]) : 32; if (isNaN(netmask)) { throw new Error(`${cidr} don't have valid netmask`); } const startIpL = ipv42num(addressParts[0]) & createIPv4NetMask(netmask); const endIpL = Math.pow(2, 32 - netmask) + startIpL - 1; return [num2ipv4(startIpL), num2ipv4(endIpL)];}
function cidr2ipv6range(cidr: string, addressParts: string[]): string[] { const netmask = addressParts.length == 2 ? Number(addressParts[1]) : 128; if (isNaN(netmask)) { throw new Error(`${cidr} don't have valid netmask`); } const startIpL = ipv62num(addressParts[0]) & createIPv6NetMask(netmask); const endIpL = BigInt(Math.pow(2, 128 - netmask)) + startIpL - BigInt(1); return [num2ipv6(startIpL), num2ipv6(endIpL)];}
/** range2cidr converts range of IP address to CIDR. */export function range2cidr(ipRange: string[]): string[] { if (ipRange.length != 2) { throw new Error( `ipRange contains only two ip address, length is ${ipRange.length}`, ); } const startIp = ipRange[0]; const endIp = ipRange[1]; if (!(isIP(startIp) && isIP(endIp))) { throw new Error( `both startIp and endIp must be ip address, start ip is ${startIp}, end ip is ${endIp}`, ); }
if (isIPv4(startIp) && isIPv4(endIp)) { return range2ipv4cidr(startIp, endIp); }
if (isIPv6(startIp) && isIPv6(endIp)) { return range2ipv6cidr(startIp, endIp); }
return [];}
function range2ipv4cidr(startIp: string, endIp: string): string[] { let startIpL = ipv42num(startIp); const endIpL = ipv42num(endIp);
const cidr = []; // https://blog.ip2location.com/knowledge-base/how-to-convert-ip-address-range-into-cidr/ while (endIpL >= startIpL) { let maxSize = 32; while (maxSize > 0) { const mask = createIPv4NetMask(maxSize - 1); if ((startIpL & mask) != startIpL) { break; } maxSize -= 1; } const diff = 32 - Math.floor(Math.log2(endIpL - startIpL + 1)); if (maxSize < diff) { maxSize = diff; } cidr.push(`${num2ipv4(startIpL)}/${maxSize}`); startIpL += Math.pow(2, 32 - maxSize); } return cidr;}
function range2ipv6cidr(startIp: string, endIp: string): string[] { let startIpL = ipv62num(startIp); const endIpL = ipv62num(endIp);
const cidr = []; // https://blog.ip2location.com/knowledge-base/how-to-convert-ip-address-range-into-cidr/ while (endIpL >= startIpL) { let maxSize = 128; while (maxSize > 0) { const mask = createIPv6NetMask(maxSize - 1); if ((startIpL & mask) != startIpL) { break; } maxSize -= 1; } const diff = 128 - ((endIpL - startIpL + BigInt(1)).toString(2).length - 1); if (maxSize < diff) { maxSize = diff; } cidr.push(`${num2ipv6(startIpL)}/${maxSize}`); startIpL += BigInt(Math.pow(2, 128 - maxSize)); } return cidr;}
/** ipv42num converts IP address to number. */export function ipv42num(ipAddress: string): number { let shift = 0; let digit = 0; let octet = 0; let sum = 0; for (const char of ipAddress) { if (char == ".") { sum += octet << (8 * (3 - shift)); shift += 1; digit = 0; octet = 0; continue; } if (digit > 0) { octet *= 10; } octet += Number(char); digit += 1; } return sum + octet;}
/** num2ipv4 converts number to IPv4 address. */export function num2ipv4(num: number): string { return [ (num >> 24) & 255, (num >> 16) & 255, (num >> 8) & 255, num & 255, ].join(".");}
function createIPv4NetMask(size: number): number { return Math.pow(2, 32) - Math.pow(2, 32 - size);}
function createIPv6NetMask(size: number): bigint { return BigInt(Math.pow(2, 128)) - BigInt(Math.pow(2, 128 - size));}
export function ipv62num(ipAddress: string): bigint { let shift = 0; let digit = 0; let hextet = 0; let sum = BigInt(0); for (const char of ipAddress) { if (char == ":") { sum += BigInt(hextet * Math.pow(2, 16 * (7 - shift))); shift += 1; digit = 0; hextet = 0; continue; } if (digit > 0) { hextet *= 16; } hextet += Number(`0x${char}`); digit += 1; } sum += BigInt(hextet); return sum;}
export function num2ipv6(num: bigint): string { const base = BigInt(Math.pow(2, 16) - 1); const bitShift = (shift: number): bigint => (num / BigInt(Math.pow(2, shift))) & base; const toHex = (shifted: bigint): string => { const hex = shifted.toString(16); return hex; }; const b1 = toHex(bitShift(112)); const b2 = toHex(bitShift(96)); const b3 = toHex(bitShift(80)); const b4 = toHex(bitShift(64)); const b5 = toHex(bitShift(48)); const b6 = toHex(bitShift(32)); const b7 = toHex(bitShift(16)); const b8 = toHex(num & base); const ipv6 = [b1]; if (b2 === "0") { ipv6.push(""); } else { ipv6.push(b2); } const pushHex = (hex: string, array: string[]) => { if (hex === "0") { if (ipv6[ipv6.length - 1] !== "") { array.push(""); } } else { array.push(hex); } }; pushHex(b3, ipv6); pushHex(b4, ipv6); pushHex(b5, ipv6); pushHex(b6, ipv6); pushHex(b7, ipv6); if (b8 === "0") { if (ipv6[ipv6.length - 1] !== "") { ipv6.push("0"); } else { ipv6.push(""); } } else { ipv6.push(b8); } return ipv6.join(":");}