UNPKG

cidr-block

Version:

IPv4 and IPv6 address and cidr range utilities

388 lines (385 loc) 13.6 kB
import { Ipv6Address } from './ipv6-address.js'; import { Ipv6Cidr } from './ipv6-cidr.js'; import { InvalidIpv6AddressError } from './ipv6-errors.js'; export { InvalidIpv6CidrError, InvalidIpv6CidrRangeError } from './ipv6-errors.js'; var ipv6; (function (ipv6) { /** * The maximum possible value for an IPv6 address. */ ipv6.MAX_SIZE = (1n << 128n) - 1n; /** * The minimum possible value for an IPv6 address. */ ipv6.MIN_SIZE = 0n; /** * The maximum CIDR range for IPv6 addresses. */ ipv6.MAX_RANGE = 128; /** * The minimum CIDR range for IPv6 addresses. */ ipv6.MIN_RANGE = 0; /** * Creates a new Ipv6Address instance from the given literal. * Valid formats include string (with :: compression support), bigint, or hextets array. * * @example Creating addresses * ```ts * import { ipv6 } from 'cidr-block'; * * const addr1 = ipv6.address("2001:db8::1"); * const addr2 = ipv6.address(42540766411282592856903984951653826561n); * const addr3 = ipv6.address([0x2001, 0xdb8, 0, 0, 0, 0, 0, 1]); * ``` * * @example Converting between formats * ```ts * import { ipv6 } from 'cidr-block'; * * const addr = ipv6.address("2001:db8::1"); * addr.toString(); // "2001:db8::1" (compressed) * addr.toFullString(); // "2001:0db8:0000:0000:0000:0000:0000:0001" * addr.toBigInt(); // 42540766411282592856903984951653826561n * addr.hextets(); // [0x2001, 0xdb8, 0, 0, 0, 0, 0, 1] * ``` * * @example Comparing addresses * ```ts * import { ipv6 } from 'cidr-block'; * * const addr = ipv6.address("2001:db8::1"); * addr.equals("2001:db8::1"); // true * addr.isGreaterThan("2001:db8::"); // true * addr.isLessThan("2001:db8::2"); // true * ``` * * @example Navigating sequential addresses * ```ts * import { ipv6 } from 'cidr-block'; * * const addr = ipv6.address("2001:db8::1"); * addr.nextAddress()?.toString(); // "2001:db8::2" * addr.previousAddress()?.toString(); // "2001:db8::" * ``` * * @example Checking address types * ```ts * import { ipv6 } from 'cidr-block'; * * ipv6.address("::1").isLoopbackAddress(); // true * ipv6.address("fc00::1").isUniqueLocalAddress(); // true * ipv6.address("fe80::1").isLinkLocalAddress(); // true * ipv6.address("ff02::1").isMulticastAddress(); // true * ``` * * @param ip - The IPv6 address in string, bigint, or hextets array format. * @returns A new Ipv6Address instance. * @throws {InvalidIpv6AddressError} If the input is not a valid IPv6 address. */ function address(ip) { return new Ipv6Address(ip); } ipv6.address = address; /** * Creates a new Ipv6Cidr instance from the given literal. * Valid formats include string, object, or tuple. * * @example Creating CIDR blocks * ```ts * import { ipv6 } from 'cidr-block'; * * const cidr1 = ipv6.cidr("2001:db8::/32"); * const cidr2 = ipv6.cidr({ address: "2001:db8::", range: 32 }); * const cidr3 = ipv6.cidr(["2001:db8::", 32]); * ``` * * @example Getting CIDR properties * ```ts * import { ipv6 } from 'cidr-block'; * * const cidr = ipv6.cidr("2001:db8::/32"); * cidr.baseAddress().toString(); // "2001:db8::" * cidr.range(); // 32 * cidr.netmask().toString(); // "ffff:ffff::" * cidr.addressCount(); // 79228162514264337593543950336n * ``` * * @example Working with usable addresses * ```ts * import { ipv6 } from 'cidr-block'; * * const cidr = ipv6.cidr("2001:db8::/126"); * cidr.getFirstUsableAddress()?.toString(); // "2001:db8::1" * cidr.getLastUsableAddress()?.toString(); // "2001:db8::2" * ``` * * @example Checking containment and overlap * ```ts * import { ipv6 } from 'cidr-block'; * * const cidr = ipv6.cidr("2001:db8::/32"); * cidr.includes(ipv6.address("2001:db8::1")); // true * cidr.includes(ipv6.address("2001:db9::1")); // false * cidr.overlaps("2001:db8::/48"); // true * cidr.overlaps("2001:db9::/32"); // false * ``` * * @example Splitting into subranges * ```ts * import { ipv6 } from 'cidr-block'; * * const cidr = ipv6.cidr("2001:db8::/32"); * * // Split into equal /34 subnets * cidr.subnet(34).map(s => s.toString()); * // ["2001:db8::/34", "2001:db8:4000::/34", "2001:db8:8000::/34", "2001:db8:c000::/34"] * ``` * * @example Navigating sequential CIDRs * ```ts * import { ipv6 } from 'cidr-block'; * * const cidr = ipv6.cidr("2001:db8::/32"); * cidr.nextCIDR()?.toString(); // "2001:db9::/32" * cidr.previousCIDR()?.toString(); // "2001:db7::/32" * ``` * * @param cidr - The IPv6 CIDR in string, object, or tuple format. * @returns A new Ipv6Cidr instance. * @throws {InvalidIpv6CidrError} If the input is not a valid IPv6 CIDR. */ function cidr(cidr) { return new Ipv6Cidr(cidr); } ipv6.cidr = cidr; /** * Validates whether the given input is a valid IPv6 address. * * @example * ```ts * import { ipv6 } from 'cidr-block'; * * ipv6.isValidAddress("2001:db8::1"); // true * ipv6.isValidAddress("::"); // true * ipv6.isValidAddress("::1"); // true * ipv6.isValidAddress("gggg::1"); // false (invalid hex) * ipv6.isValidAddress("2001:db8"); // false (incomplete) * ipv6.isValidAddress([0x2001, 0xdb8, 0, 0, 0, 0, 0, 1]); // true * ``` * * @param ip - The IPv6 address to validate, which can be in string, bigint, or hextets array format. * @returns True if the input is a valid IPv6 address; otherwise, false. */ function isValidAddress(ip) { if (ip === null || ip === undefined) { return false; } if (typeof ip === 'bigint') { return ip >= ipv6.MIN_SIZE && ip <= ipv6.MAX_SIZE; } if (Array.isArray(ip)) { if (ip.length !== 8) { return false; } for (const element of ip) { if (typeof element !== 'number' || !Number.isInteger(element) || element < 0 || element > 0xffff) { return false; } } return true; } if (typeof ip !== 'string') { return false; } // Handle IPv4-mapped IPv6 addresses (::ffff:192.168.1.1) const ipv4MappedMatch = ip.match(/^(::ffff:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i); if (ipv4MappedMatch) { const ipv4Part = ipv4MappedMatch[2]; const octets = ipv4Part.split('.').map(Number); if (octets.length !== 4) return false; for (const octet of octets) { if (octet < 0 || octet > 255) return false; } return true; } // Check for multiple :: which is invalid const doubleColonCount = (ip.match(/::/g) || []).length; if (doubleColonCount > 1) { return false; } // Handle :: expansion let expandedIp = ip; if (ip.includes('::')) { const parts = ip.split('::'); const leftParts = parts[0] ? parts[0].split(':') : []; const rightParts = parts[1] ? parts[1].split(':') : []; const missingCount = 8 - leftParts.length - rightParts.length; if (missingCount < 0) { return false; } const middleParts = Array(missingCount).fill('0'); expandedIp = [...leftParts, ...middleParts, ...rightParts].join(':'); } const hextets = expandedIp.split(':'); if (hextets.length !== 8) { return false; } for (const hextet of hextets) { if (hextet === '' || hextet.length > 4) { return false; } if (!/^[0-9a-fA-F]+$/.test(hextet)) { return false; } const value = Number.parseInt(hextet, 16); if (value < 0 || value > 0xffff) { return false; } } return true; } ipv6.isValidAddress = isValidAddress; /** * Validates whether the given input is a valid IPv6 CIDR. * * @example * ```ts * import { ipv6 } from 'cidr-block'; * * ipv6.isValidCIDR("2001:db8::/32"); // true * ipv6.isValidCIDR("2001:db8::/129"); // false (range out of bounds) * ipv6.isValidCIDR({ address: "2001:db8::", range: 32 }); // true * ipv6.isValidCIDR(["2001:db8::", 32]); // true * ipv6.isValidCIDR("invalid/32"); // false * ``` * * @param cidr - The IPv6 CIDR to validate, which can be in string, object, or tuple format. * @returns True if the input is a valid IPv6 CIDR; otherwise, false. */ function isValidCIDR(cidr) { if (cidr === null || cidr === undefined) { return false; } let address; let range; if (typeof cidr === 'string') { const parts = cidr.split('/'); if (parts.length !== 2) { return false; } address = parts[0]; range = Number.parseInt(parts[1], 10); } else if (Array.isArray(cidr)) { if (cidr.length !== 2) { return false; } const [first, second] = cidr; if (first === null || first === undefined) { return false; } if (typeof second !== 'number') { return false; } address = first; range = second; } else if (typeof cidr === 'object') { if (!('address' in cidr) || !('range' in cidr)) { return false; } const { address: addr, range: r } = cidr; if (addr === null || addr === undefined) { return false; } if (typeof r !== 'number') { return false; } address = addr; range = r; } else { return false; } if (address === undefined || !isValidAddress(address)) { return false; } if (typeof range !== 'number' || !Number.isInteger(range) || range < ipv6.MIN_RANGE || range > ipv6.MAX_RANGE) { return false; } return true; } ipv6.isValidCIDR = isValidCIDR; /** * Parses the given IPv6 address into its hextets representation. * * @example * ```ts * import { ipv6 } from 'cidr-block'; * * ipv6.parseHextets("2001:db8::1"); // [0x2001, 0xdb8, 0, 0, 0, 0, 0, 1] * ipv6.parseHextets("::"); // [0, 0, 0, 0, 0, 0, 0, 0] * ipv6.parseHextets(42540766411282592856903984951653826561n); // [0x2001, 0xdb8, 0, 0, 0, 0, 0, 1] * ``` * * @param ip - The IPv6 address to parse, which can be in string, bigint, or hextets array format. * @returns {Ipv6AddressHextets} An array of eight numbers representing the hextets of the IPv6 address. * @throws {InvalidIpv6AddressError} If the input is not a valid IPv6 address. */ function parseHextets(ip) { if (!isValidAddress(ip)) { throw new InvalidIpv6AddressError(ip); } if (typeof ip === 'bigint') { const hextets = []; let value = ip; for (let i = 0; i < 8; i++) { hextets.unshift(Number(value & 0xffffn)); value = value >> 16n; } return hextets; } if (Array.isArray(ip)) { return ip; } // Handle IPv4-mapped IPv6 addresses const ipv4MappedMatch = ip.match(/^(::ffff:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i); if (ipv4MappedMatch) { const ipv4Part = ipv4MappedMatch[2]; const octets = ipv4Part.split('.').map(Number); return [ 0, 0, 0, 0, 0, 0xffff, (octets[0] << 8) | octets[1], (octets[2] << 8) | octets[3], ]; } // Handle :: expansion let expandedIp = ip; if (ip.includes('::')) { const parts = ip.split('::'); const leftParts = parts[0] ? parts[0].split(':') : []; const rightParts = parts[1] ? parts[1].split(':') : []; const missingCount = 8 - leftParts.length - rightParts.length; const middleParts = Array(missingCount).fill('0'); expandedIp = [...leftParts, ...middleParts, ...rightParts].join(':'); } return expandedIp.split(':').map(h => Number.parseInt(h, 16)); } ipv6.parseHextets = parseHextets; })(ipv6 || (ipv6 = {})); export { InvalidIpv6AddressError, Ipv6Address, Ipv6Cidr, ipv6 }; //# sourceMappingURL=ipv6.js.map