diff --git a/lib/ipaddr.d.ts b/lib/ipaddr.d.ts index ee02c02..0e1026c 100644 --- a/lib/ipaddr.d.ts +++ b/lib/ipaddr.d.ts @@ -1,72 +1,66 @@ -declare module "ipaddr.js" { - type IPvXRangeDefaults = 'unicast' | 'unspecified' | 'multicast' | 'linkLocal' | 'loopback' | 'reserved' | 'benchmarking' | 'amt'; - type IPv4Range = IPvXRangeDefaults | 'broadcast' | 'carrierGradeNat' | 'private' | 'as112'; - type IPv6Range = IPvXRangeDefaults | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo' | 'as112v6' | 'orchid2' | 'droneRemoteIdProtocolEntityTags'; +export type IPvXRangeDefaults = 'unicast' | 'unspecified' | 'multicast' | 'linkLocal' | 'loopback' | 'reserved' | 'benchmarking' | 'amt'; +export type IPv4Range = IPvXRangeDefaults | 'broadcast' | 'carrierGradeNat' | 'private' | 'as112'; +export type IPv6Range = IPvXRangeDefaults | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo' | 'as112v6' | 'orchid2' | 'droneRemoteIdProtocolEntityTags'; - interface RangeList { - [name: string]: [T, number] | [T, number][]; - } - - // Common methods/properties for IPv4 and IPv6 classes. - class IP { - prefixLengthFromSubnetMask(): number | null; - toByteArray(): number[]; - toNormalizedString(): string; - toString(): string; - } +export interface RangeList { + [name: string]: [T, number] | [T, number][]; +} - namespace Address { - export function fromByteArray(bytes: number[]): IPv4 | IPv6; - export function isValid(addr: string): boolean; - export function isValidCIDR(addr: string): boolean; - export function parse(addr: string): IPv4 | IPv6; - export function parseCIDR(mask: string): [IPv4 | IPv6, number]; - export function process(addr: string): IPv4 | IPv6; - export function subnetMatch(addr: IPv4 | IPv6, rangeList: RangeList, defaultName?: string): string; +// Common methods/properties for IPv4 and IPv6 classes. +export class IP { + prefixLengthFromSubnetMask(): number | null; + toByteArray(): number[]; + toNormalizedString(): string; + toString(): string; +} - export class IPv4 extends IP { - static broadcastAddressFromCIDR(addr: string): IPv4; - static isIPv4(addr: string): boolean; - static isValid(addr: string): boolean; - static isValidCIDR(addr: string): boolean; - static isValidFourPartDecimal(addr: string): boolean; - static isValidCIDRFourPartDecimal(addr: string): boolean; - static networkAddressFromCIDR(addr: string): IPv4; - static parse(addr: string): IPv4; - static parseCIDR(addr: string): [IPv4, number]; - static subnetMaskFromPrefixLength(prefix: number): IPv4; - constructor(octets: number[]); - octets: number[] +export function fromByteArray(bytes: number[]): IPv4 | IPv6; +export function isValid(addr: string): boolean; +export function isValidCIDR(addr: string): boolean; +export function parse(addr: string): IPv4 | IPv6; +export function parseCIDR(mask: string): [IPv4 | IPv6, number]; +export function process(addr: string): IPv4 | IPv6; +export function subnetMatch(addr: IPv4 | IPv6, rangeList: RangeList, defaultName?: string): string; - kind(): 'ipv4'; - match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; - range(): IPv4Range; - subnetMatch(rangeList: RangeList, defaultName?: string): string; - toIPv4MappedAddress(): IPv6; - } +export class IPv4 extends IP { + static broadcastAddressFromCIDR(addr: string): IPv4; + static isIPv4(addr: string): boolean; + static isValid(addr: string): boolean; + static isValidCIDR(addr: string): boolean; + static isValidFourPartDecimal(addr: string): boolean; + static isValidCIDRFourPartDecimal(addr: string): boolean; + static networkAddressFromCIDR(addr: string): IPv4; + static parse(addr: string): IPv4; + static parseCIDR(addr: string): [IPv4, number]; + static subnetMaskFromPrefixLength(prefix: number): IPv4; + constructor(octets: number[]); + octets: number[] - export class IPv6 extends IP { - static broadcastAddressFromCIDR(addr: string): IPv6; - static isIPv6(addr: string): boolean; - static isValid(addr: string): boolean; - static isValidCIDR(addr: string): boolean; - static networkAddressFromCIDR(addr: string): IPv6; - static parse(addr: string): IPv6; - static parseCIDR(addr: string): [IPv6, number]; - static subnetMaskFromPrefixLength(prefix: number): IPv6; - constructor(parts: number[]); - parts: number[] - zoneId?: string + kind(): 'ipv4'; + match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; + range(): IPv4Range; + subnetMatch(rangeList: RangeList, defaultName?: string): string; + toIPv4MappedAddress(): IPv6; +} - isIPv4MappedAddress(): boolean; - kind(): 'ipv6'; - match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; - range(): IPv6Range; - subnetMatch(rangeList: RangeList, defaultName?: string): string; - toIPv4Address(): IPv4; - toRFC5952String(): string; - } - } +export class IPv6 extends IP { + static broadcastAddressFromCIDR(addr: string): IPv6; + static isIPv6(addr: string): boolean; + static isValid(addr: string): boolean; + static isValidCIDR(addr: string): boolean; + static networkAddressFromCIDR(addr: string): IPv6; + static parse(addr: string): IPv6; + static parseCIDR(addr: string): [IPv6, number]; + static subnetMaskFromPrefixLength(prefix: number): IPv6; + constructor(parts: number[]); + parts: number[] + zoneId?: string - export = Address; -} + isIPv4MappedAddress(): boolean; + kind(): 'ipv6'; + match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; + range(): IPv6Range; + subnetMatch(rangeList: RangeList, defaultName?: string): string; + toIPv4Address(): IPv4; + toRFC5952String(): string; +} \ No newline at end of file diff --git a/lib/ipaddr.js b/lib/ipaddr.js index f821948..da06924 100644 --- a/lib/ipaddr.js +++ b/lib/ipaddr.js @@ -1,318 +1,313 @@ -(function (root) { - 'use strict'; - // A list of regular expressions that match arbitrary IPv4 addresses, - // for which a number of weird notations exist. - // Note that an address like 0010.0xa5.1.1 is considered legal. - const ipv4Part = '(0?\\d+|0x[a-f0-9]+)'; - const ipv4Regexes = { - fourOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'), - threeOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'), - twoOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}$`, 'i'), - longValue: new RegExp(`^${ipv4Part}$`, 'i') - }; +// A list of regular expressions that match arbitrary IPv4 addresses, +// for which a number of weird notations exist. +// Note that an address like 0010.0xa5.1.1 is considered legal. +const ipv4Part = '(0?\\d+|0x[a-f0-9]+)'; +const ipv4Regexes = { + fourOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'), + threeOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'), + twoOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}$`, 'i'), + longValue: new RegExp(`^${ipv4Part}$`, 'i') +}; + +// Regular Expression for checking Octal numbers +const octalRegex = new RegExp(`^0[0-7]+$`, 'i'); +const hexRegex = new RegExp(`^0x[a-f0-9]+$`, 'i'); + +const zoneIndex = '%[0-9a-z]{1,}'; + +// IPv6-matching regular expressions. +// For IPv6, the task is simpler: it is enough to match the colon-delimited +// hexadecimal IPv6 and a transitional variant with dotted-decimal IPv4 at +// the end. +const ipv6Part = '(?:[0-9a-f]+::?)+'; +const ipv6Regexes = { + zoneIndex: new RegExp(zoneIndex, 'i'), + 'native': new RegExp(`^(::)?(${ipv6Part})?([0-9a-f]+)?(::)?(${zoneIndex})?$`, 'i'), + deprecatedTransitional: new RegExp(`^(?:::)(${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?)$`, 'i'), + transitional: new RegExp(`^((?:${ipv6Part})|(?:::)(?:${ipv6Part})?)${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?$`, 'i') +}; + +// Expand :: in an IPv6 address or address part consisting of `parts` groups. +function expandIPv6 (string, parts) { + // More than one '::' means invalid address + if (string.indexOf('::') !== string.lastIndexOf('::')) { + return null; + } - // Regular Expression for checking Octal numbers - const octalRegex = new RegExp(`^0[0-7]+$`, 'i'); - const hexRegex = new RegExp(`^0x[a-f0-9]+$`, 'i'); - - const zoneIndex = '%[0-9a-z]{1,}'; - - // IPv6-matching regular expressions. - // For IPv6, the task is simpler: it is enough to match the colon-delimited - // hexadecimal IPv6 and a transitional variant with dotted-decimal IPv4 at - // the end. - const ipv6Part = '(?:[0-9a-f]+::?)+'; - const ipv6Regexes = { - zoneIndex: new RegExp(zoneIndex, 'i'), - 'native': new RegExp(`^(::)?(${ipv6Part})?([0-9a-f]+)?(::)?(${zoneIndex})?$`, 'i'), - deprecatedTransitional: new RegExp(`^(?:::)(${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?)$`, 'i'), - transitional: new RegExp(`^((?:${ipv6Part})|(?:::)(?:${ipv6Part})?)${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?$`, 'i') - }; + let colonCount = 0; + let lastColon = -1; + let zoneId = (string.match(ipv6Regexes.zoneIndex) || [])[0]; + let replacement, replacementCount; - // Expand :: in an IPv6 address or address part consisting of `parts` groups. - function expandIPv6 (string, parts) { - // More than one '::' means invalid address - if (string.indexOf('::') !== string.lastIndexOf('::')) { - return null; - } + // Remove zone index and save it for later + if (zoneId) { + zoneId = zoneId.substring(1); + string = string.replace(/%.+$/, ''); + } - let colonCount = 0; - let lastColon = -1; - let zoneId = (string.match(ipv6Regexes.zoneIndex) || [])[0]; - let replacement, replacementCount; + // How many parts do we already have? + while ((lastColon = string.indexOf(':', lastColon + 1)) >= 0) { + colonCount++; + } - // Remove zone index and save it for later - if (zoneId) { - zoneId = zoneId.substring(1); - string = string.replace(/%.+$/, ''); - } + // 0::0 is two parts more than :: + if (string.substr(0, 2) === '::') { + colonCount--; + } - // How many parts do we already have? - while ((lastColon = string.indexOf(':', lastColon + 1)) >= 0) { - colonCount++; - } + if (string.substr(-2, 2) === '::') { + colonCount--; + } - // 0::0 is two parts more than :: - if (string.substr(0, 2) === '::') { - colonCount--; - } + // An address must not contain more separators than available parts, + // and :: must compress at least one part. + if (colonCount >= parts) { + return null; + } - if (string.substr(-2, 2) === '::') { - colonCount--; - } + // replacement = ':' + '0:' * (parts - colonCount) + replacementCount = parts - colonCount; + replacement = ':'; + while (replacementCount--) { + replacement += '0:'; + } - // An address must not contain more separators than available parts, - // and :: must compress at least one part. - if (colonCount >= parts) { - return null; - } + // Insert the missing zeroes + string = string.replace('::', replacement); - // replacement = ':' + '0:' * (parts - colonCount) - replacementCount = parts - colonCount; - replacement = ':'; - while (replacementCount--) { - replacement += '0:'; - } + // Trim any garbage which may be hanging around if :: was at the edge in + // the source string + if (string[0] === ':') { + string = string.slice(1); + } - // Insert the missing zeroes - string = string.replace('::', replacement); + if (string[string.length - 1] === ':') { + string = string.slice(0, -1); + } - // Trim any garbage which may be hanging around if :: was at the edge in - // the source string - if (string[0] === ':') { - string = string.slice(1); - } + parts = (function () { + const ref = string.split(':'); + const results = []; - if (string[string.length - 1] === ':') { - string = string.slice(0, -1); + for (let i = 0; i < ref.length; i++) { + results.push(ref[i].length > 4 ? NaN : parseInt(ref[i], 16)); } - parts = (function () { - const ref = string.split(':'); - const results = []; - - for (let i = 0; i < ref.length; i++) { - results.push(ref[i].length > 4 ? NaN : parseInt(ref[i], 16)); - } + return results; + })(); - return results; - })(); + return { + parts: parts, + zoneId: zoneId + }; +} - return { - parts: parts, - zoneId: zoneId - }; +// A generic CIDR (Classless Inter-Domain Routing) RFC1518 range matcher. +function matchCIDR (first, second, partSize, cidrBits) { + if (first.length !== second.length) { + throw new Error('ipaddr: cannot match CIDR for objects with different lengths'); } - // A generic CIDR (Classless Inter-Domain Routing) RFC1518 range matcher. - function matchCIDR (first, second, partSize, cidrBits) { - if (first.length !== second.length) { - throw new Error('ipaddr: cannot match CIDR for objects with different lengths'); - } + let part = 0; + let shift; - let part = 0; - let shift; - - while (cidrBits > 0) { - shift = partSize - cidrBits; - if (shift < 0) { - shift = 0; - } - - if (first[part] >> shift !== second[part] >> shift) { - return false; - } - - cidrBits -= partSize; - part += 1; + while (cidrBits > 0) { + shift = partSize - cidrBits; + if (shift < 0) { + shift = 0; } - return true; - } - - function parseIntAuto (string) { - // Hexadecimal base 16 (0x#) - if (hexRegex.test(string)) { - return parseInt(string, 16); - } - // While octal representation is discouraged by ECMAScript 3 - // and forbidden by ECMAScript 5, we silently allow it to - // work only if the rest of the string has numbers less than 8. - if (string[0] === '0' && !isNaN(parseInt(string[1], 10))) { - if (octalRegex.test(string)) { - return parseInt(string, 8); - } - throw new Error(`ipaddr: cannot parse ${string} as octal`); + if (first[part] >> shift !== second[part] >> shift) { + return false; } - // Always include the base 10 radix! - return parseInt(string, 10); + + cidrBits -= partSize; + part += 1; } - function padPart (part, length) { - while (part.length < length) { - part = `0${part}`; - } + return true; +} - return part; +function parseIntAuto (string) { + // Hexadecimal base 16 (0x#) + if (hexRegex.test(string)) { + return parseInt(string, 16); } + // While octal representation is discouraged by ECMAScript 3 + // and forbidden by ECMAScript 5, we silently allow it to + // work only if the rest of the string has numbers less than 8. + if (string[0] === '0' && !isNaN(parseInt(string[1], 10))) { + if (octalRegex.test(string)) { + return parseInt(string, 8); + } + throw new Error(`ipaddr: cannot parse ${string} as octal`); + } + // Always include the base 10 radix! + return parseInt(string, 10); +} - const ipaddr = {}; +function padPart (part, length) { + while (part.length < length) { + part = `0${part}`; + } - // An IPv4 address (RFC791). - ipaddr.IPv4 = (function () { - // Constructs a new IPv4 address from an array of four octets - // in network order (MSB first) - // Verifies the input. - function IPv4 (octets) { - if (octets.length !== 4) { - throw new Error('ipaddr: ipv4 octet count should be 4'); - } + return part; +} + +// An IPv4 address (RFC791). +export class IPv4 { + /** @type number[] */ + octets + // Constructs a new IPv4 address from an array of four octets + // in network order (MSB first) + // Verifies the input. + constructor(octets) { + if (octets.length !== 4) { + throw new Error('ipaddr: ipv4 octet count should be 4'); + } - let i, octet; + let i, octet; - for (i = 0; i < octets.length; i++) { - octet = octets[i]; - if (!((0 <= octet && octet <= 255))) { - throw new Error('ipaddr: ipv4 octet should fit in 8 bits'); - } + for (i = 0; i < octets.length; i++) { + octet = octets[i]; + if (!((0 <= octet && octet <= 255))) { + throw new Error('ipaddr: ipv4 octet should fit in 8 bits'); } - - this.octets = octets; } - // Special IPv4 address ranges. - // See also https://en.wikipedia.org/wiki/Reserved_IP_addresses - IPv4.prototype.SpecialRanges = { - unspecified: [[new IPv4([0, 0, 0, 0]), 8]], - broadcast: [[new IPv4([255, 255, 255, 255]), 32]], - // RFC3171 - multicast: [[new IPv4([224, 0, 0, 0]), 4]], - // RFC3927 - linkLocal: [[new IPv4([169, 254, 0, 0]), 16]], - // RFC5735 - loopback: [[new IPv4([127, 0, 0, 0]), 8]], - // RFC6598 - carrierGradeNat: [[new IPv4([100, 64, 0, 0]), 10]], - // RFC1918 - 'private': [ - [new IPv4([10, 0, 0, 0]), 8], - [new IPv4([172, 16, 0, 0]), 12], - [new IPv4([192, 168, 0, 0]), 16] - ], - // Reserved and testing-only ranges; RFCs 5735, 5737, 2544, 1700 - reserved: [ - [new IPv4([192, 0, 0, 0]), 24], - [new IPv4([192, 0, 2, 0]), 24], - [new IPv4([192, 88, 99, 0]), 24], - [new IPv4([198, 18, 0, 0]), 15], - [new IPv4([198, 51, 100, 0]), 24], - [new IPv4([203, 0, 113, 0]), 24], - [new IPv4([240, 0, 0, 0]), 4] - ], - // RFC7534, RFC7535 - as112: [ - [new IPv4([192, 175, 48, 0]), 24], - [new IPv4([192, 31, 196, 0]), 24], - ], - // RFC7450 - amt: [ - [new IPv4([192, 52, 193, 0]), 24], - ], - }; + this.octets = octets; + } - // The 'kind' method exists on both IPv4 and IPv6 classes. - IPv4.prototype.kind = function () { - return 'ipv4'; - }; + // Special IPv4 address ranges. + // See also https://en.wikipedia.org/wiki/Reserved_IP_addresses + static SpecialRanges = { + unspecified: [[new IPv4([0, 0, 0, 0]), 8]], + broadcast: [[new IPv4([255, 255, 255, 255]), 32]], + // RFC3171 + multicast: [[new IPv4([224, 0, 0, 0]), 4]], + // RFC3927 + linkLocal: [[new IPv4([169, 254, 0, 0]), 16]], + // RFC5735 + loopback: [[new IPv4([127, 0, 0, 0]), 8]], + // RFC6598 + carrierGradeNat: [[new IPv4([100, 64, 0, 0]), 10]], + // RFC1918 + 'private': [ + [new IPv4([10, 0, 0, 0]), 8], + [new IPv4([172, 16, 0, 0]), 12], + [new IPv4([192, 168, 0, 0]), 16] + ], + // Reserved and testing-only ranges; RFCs 5735, 5737, 2544, 1700 + reserved: [ + [new IPv4([192, 0, 0, 0]), 24], + [new IPv4([192, 0, 2, 0]), 24], + [new IPv4([192, 88, 99, 0]), 24], + [new IPv4([198, 18, 0, 0]), 15], + [new IPv4([198, 51, 100, 0]), 24], + [new IPv4([203, 0, 113, 0]), 24], + [new IPv4([240, 0, 0, 0]), 4] + ], + // RFC7534, RFC7535 + as112: [ + [new IPv4([192, 175, 48, 0]), 24], + [new IPv4([192, 31, 196, 0]), 24], + ], + // RFC7450 + amt: [ + [new IPv4([192, 52, 193, 0]), 24], + ], + }; - // Checks if this address matches other one within given CIDR range. - IPv4.prototype.match = function (other, cidrRange) { - let ref; - if (cidrRange === undefined) { - ref = other; - other = ref[0]; - cidrRange = ref[1]; - } + // The 'kind' method exists on both IPv4 and IPv6 classes. + kind() { + return 'ipv4'; + }; - if (other.kind() !== 'ipv4') { - throw new Error('ipaddr: cannot match ipv4 address with non-ipv4 one'); - } + // Checks if this address matches other one within given CIDR range. + match(other, cidrRange) { + let ref; + if (cidrRange === undefined) { + ref = other; + other = ref[0]; + cidrRange = ref[1]; + } - return matchCIDR(this.octets, other.octets, 8, cidrRange); - }; + if (other.kind() !== 'ipv4') { + throw new Error('ipaddr: cannot match ipv4 address with non-ipv4 one'); + } - // returns a number of leading ones in IPv4 address, making sure that - // the rest is a solid sequence of 0's (valid netmask) - // returns either the CIDR length or null if mask is not valid - IPv4.prototype.prefixLengthFromSubnetMask = function () { - let cidr = 0; - // non-zero encountered stop scanning for zeroes - let stop = false; - // number of zeroes in octet - const zerotable = { - 0: 8, - 128: 7, - 192: 6, - 224: 5, - 240: 4, - 248: 3, - 252: 2, - 254: 1, - 255: 0 - }; - let i, octet, zeros; - - for (i = 3; i >= 0; i -= 1) { - octet = this.octets[i]; - if (octet in zerotable) { - zeros = zerotable[octet]; - if (stop && zeros !== 0) { - return null; - } + return matchCIDR(this.octets, other.octets, 8, cidrRange); + }; - if (zeros !== 8) { - stop = true; - } + // returns a number of leading ones in IPv4 address, making sure that + // the rest is a solid sequence of 0's (valid netmask) + // returns either the CIDR length or null if mask is not valid + prefixLengthFromSubnetMask() { + let cidr = 0; + // non-zero encountered stop scanning for zeroes + let stop = false; + // number of zeroes in octet + const zerotable = { + 0: 8, + 128: 7, + 192: 6, + 224: 5, + 240: 4, + 248: 3, + 252: 2, + 254: 1, + 255: 0 + }; + let i, octet, zeros; - cidr += zeros; - } else { + for (i = 3; i >= 0; i -= 1) { + octet = this.octets[i]; + if (octet in zerotable) { + zeros = zerotable[octet]; + if (stop && zeros !== 0) { return null; } - } - return 32 - cidr; - }; + if (zeros !== 8) { + stop = true; + } - // Checks if the address corresponds to one of the special ranges. - IPv4.prototype.range = function () { - return ipaddr.subnetMatch(this, this.SpecialRanges); - }; + cidr += zeros; + } else { + return null; + } + } - // Returns an array of byte-sized values in network order (MSB first) - IPv4.prototype.toByteArray = function () { - return this.octets.slice(0); - }; + return 32 - cidr; + }; - // Converts this IPv4 address to an IPv4-mapped IPv6 address. - IPv4.prototype.toIPv4MappedAddress = function () { - return ipaddr.IPv6.parse(`::ffff:${this.toString()}`); - }; + // Checks if the address corresponds to one of the special ranges. + range() { + return subnetMatch(this, IPv4.SpecialRanges); + }; - // Symmetrical method strictly for aligning with the IPv6 methods. - IPv4.prototype.toNormalizedString = function () { - return this.toString(); - }; + // Returns an array of byte-sized values in network order (MSB first) + toByteArray() { + return this.octets.slice(0); + }; - // Returns the address in convenient, decimal-dotted format. - IPv4.prototype.toString = function () { - return this.octets.join('.'); - }; + // Converts this IPv4 address to an IPv4-mapped IPv6 address. + toIPv4MappedAddress() { + return IPv6.parse(`::ffff:${this.toString()}`); + }; - return IPv4; - })(); + // Symmetrical method strictly for aligning with the IPv6 methods. + toNormalizedString() { + return this.toString(); + }; + + // Returns the address in convenient, decimal-dotted format. + toString() { + return this.octets.join('.'); + }; // A utility function to return broadcast address given the IPv4 interface and prefix length in CIDR notation - ipaddr.IPv4.broadcastAddressFromCIDR = function (string) { + static broadcastAddressFromCIDR(string) { try { const cidr = this.parseCIDR(string); @@ -333,12 +328,12 @@ }; // Checks if a given string is formatted like IPv4 address. - ipaddr.IPv4.isIPv4 = function (string) { + static isIPv4(string) { return this.parser(string) !== null; }; // Checks if a given string is a valid IPv4 address. - ipaddr.IPv4.isValid = function (string) { + static isValid(string) { try { new this(this.parser(string)); return true; @@ -348,7 +343,7 @@ }; // Checks if a given string is a valid IPv4 address in CIDR notation. - ipaddr.IPv4.isValidCIDR = function (string) { + static isValidCIDR(string) { try { this.parseCIDR(string); return true; @@ -358,8 +353,8 @@ }; // Checks if a given string is a full four-part IPv4 Address. - ipaddr.IPv4.isValidFourPartDecimal = function (string) { - if (ipaddr.IPv4.isValid(string) && string.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/)) { + static isValidFourPartDecimal(string) { + if (IPv4.isValid(string) && string.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/)) { return true; } else { return false; @@ -367,18 +362,18 @@ }; // Checks if a given string is a full four-part IPv4 Address with CIDR prefix. - ipaddr.IPv4.isValidCIDRFourPartDecimal = function (string) { + static isValidCIDRFourPartDecimal(string) { const match = string.match(/^(.+)\/(\d+)$/); - if (!ipaddr.IPv4.isValidCIDR(string) || !match) { + if (!IPv4.isValidCIDR(string) || !match) { return false; } - return ipaddr.IPv4.isValidFourPartDecimal(match[1]); + return IPv4.isValidFourPartDecimal(match[1]); }; // A utility function to return network address given the IPv4 interface and prefix length in CIDR notation - ipaddr.IPv4.networkAddressFromCIDR = function (string) { + static networkAddressFromCIDR(string) { let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; try { @@ -401,7 +396,7 @@ // Tries to parse and validate a string with IPv4 address. // Throws an error if it fails. - ipaddr.IPv4.parse = function (string) { + static parse(string) { const parts = this.parser(string); if (parts === null) { @@ -412,7 +407,7 @@ }; // Parses the string as an IPv4 Address with CIDR Notation. - ipaddr.IPv4.parseCIDR = function (string) { + static parseCIDR(string) { let match; if ((match = string.match(/^(.+)\/(\d+)$/))) { @@ -434,7 +429,7 @@ // Classful variants (like a.b, where a is an octet, and b is a 24-bit // value representing last three octets; this corresponds to a class C // address) are omitted due to classless nature of modern Internet. - ipaddr.IPv4.parser = function (string) { + static parser(string) { let match, part, value; // parseInt recognizes all that octal & hexadecimal weirdness for us @@ -506,7 +501,7 @@ }; // A utility function to return subnet mask in IPv4 format given the prefix length - ipaddr.IPv4.subnetMaskFromPrefixLength = function (prefix) { + static subnetMaskFromPrefixLength(prefix) { prefix = parseInt(prefix); if (Number.isNaN(prefix) || prefix < 0 || prefix > 32) { throw new Error('ipaddr: invalid IPv4 prefix length'); @@ -527,299 +522,297 @@ return new this(octets); }; +} - // An IPv6 address (RFC2460) - ipaddr.IPv6 = (function () { - // Constructs an IPv6 address from an array of eight 16 - bit parts - // or sixteen 8 - bit parts in network order(MSB first). - // Throws an error if the input is invalid. - function IPv6 (parts, zoneId) { - let i, part; - - if (parts.length === 16) { - this.parts = []; - for (i = 0; i <= 14; i += 2) { - this.parts.push((parts[i] << 8) | parts[i + 1]); - } - } else if (parts.length === 8) { - this.parts = parts; - } else { - throw new Error('ipaddr: ipv6 part count should be 8 or 16'); - } - for (i = 0; i < this.parts.length; i++) { - part = this.parts[i]; - if (!((0 <= part && part <= 0xffff))) { - throw new Error('ipaddr: ipv6 part should fit in 16 bits'); - } +// An IPv6 address (RFC2460) +export class IPv6 { + // Constructs an IPv6 address from an array of eight 16 - bit parts + // or sixteen 8 - bit parts in network order(MSB first). + // Throws an error if the input is invalid. + constructor(parts, zoneId) { + let i, part; + + if (parts.length === 16) { + this.parts = []; + for (i = 0; i <= 14; i += 2) { + this.parts.push((parts[i] << 8) | parts[i + 1]); } + } else if (parts.length === 8) { + this.parts = parts; + } else { + throw new Error('ipaddr: ipv6 part count should be 8 or 16'); + } - if (zoneId) { - this.zoneId = zoneId; + for (i = 0; i < this.parts.length; i++) { + part = this.parts[i]; + if (!((0 <= part && part <= 0xffff))) { + throw new Error('ipaddr: ipv6 part should fit in 16 bits'); } } - // Special IPv6 ranges - IPv6.prototype.SpecialRanges = { - // RFC4291, here and after - unspecified: [new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128], - linkLocal: [new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10], - multicast: [new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8], - loopback: [new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128], - uniqueLocal: [new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7], - ipv4Mapped: [new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96], - // RFC3879 - deprecatedSiteLocal: [new IPv6([0xfec0, 0, 0, 0, 0, 0, 0, 0]), 10], - // RFC6666 - discard: [new IPv6([0x100, 0, 0, 0, 0, 0, 0, 0]), 64], - // RFC6145 - rfc6145: [new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96], - rfc6052: [ - // RFC6052 - [new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96], - // RFC8215 - [new IPv6([0x64, 0xff9b, 0x1, 0, 0, 0, 0, 0]), 48], - ], - // RFC3056 - '6to4': [new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16], - // RFC6052, RFC6146 - teredo: [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32], - // RFC5180 - benchmarking: [new IPv6([0x2001, 0x2, 0, 0, 0, 0, 0, 0]), 48], - // RFC7450 - amt: [new IPv6([0x2001, 0x3, 0, 0, 0, 0, 0, 0]), 32], - as112v6: [ - // RFC7535 - [new IPv6([0x2001, 0x4, 0x112, 0, 0, 0, 0, 0]), 48], - // RFC7534 - [new IPv6([0x2620, 0x4f, 0x8000, 0, 0, 0, 0, 0]), 48], - ], - // RFC4843 - deprecatedOrchid: [new IPv6([0x2001, 0x10, 0, 0, 0, 0, 0, 0]), 28], - // RFC7343 - orchid2: [new IPv6([0x2001, 0x20, 0, 0, 0, 0, 0, 0]), 28], - // RFC9374 - droneRemoteIdProtocolEntityTags: [new IPv6([0x2001, 0x30, 0, 0, 0, 0, 0, 0]), 28], - // RFC9602 - segmentRouting: [new IPv6([0x5f00, 0, 0, 0, 0, 0, 0, 0]), 16], - reserved: [ - // RFC3849 - [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 23], - // RFC2928 - [new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32], - // RFC9637 - [new IPv6([0x3fff, 0, 0, 0, 0, 0, 0, 0]), 20], - ], - }; + if (zoneId) { + this.zoneId = zoneId; + } + } - // Checks if this address is an IPv4-mapped IPv6 address. - IPv6.prototype.isIPv4MappedAddress = function () { - return this.range() === 'ipv4Mapped'; - }; + // Special IPv6 ranges + static SpecialRanges = { + // RFC4291, here and after + unspecified: [new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128], + linkLocal: [new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10], + multicast: [new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8], + loopback: [new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128], + uniqueLocal: [new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7], + ipv4Mapped: [new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96], + // RFC3879 + deprecatedSiteLocal: [new IPv6([0xfec0, 0, 0, 0, 0, 0, 0, 0]), 10], + // RFC6666 + discard: [new IPv6([0x100, 0, 0, 0, 0, 0, 0, 0]), 64], + // RFC6145 + rfc6145: [new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96], + rfc6052: [ + // RFC6052 + [new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96], + // RFC8215 + [new IPv6([0x64, 0xff9b, 0x1, 0, 0, 0, 0, 0]), 48], + ], + // RFC3056 + '6to4': [new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16], + // RFC6052, RFC6146 + teredo: [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32], + // RFC5180 + benchmarking: [new IPv6([0x2001, 0x2, 0, 0, 0, 0, 0, 0]), 48], + // RFC7450 + amt: [new IPv6([0x2001, 0x3, 0, 0, 0, 0, 0, 0]), 32], + as112v6: [ + // RFC7535 + [new IPv6([0x2001, 0x4, 0x112, 0, 0, 0, 0, 0]), 48], + // RFC7534 + [new IPv6([0x2620, 0x4f, 0x8000, 0, 0, 0, 0, 0]), 48], + ], + // RFC4843 + deprecatedOrchid: [new IPv6([0x2001, 0x10, 0, 0, 0, 0, 0, 0]), 28], + // RFC7343 + orchid2: [new IPv6([0x2001, 0x20, 0, 0, 0, 0, 0, 0]), 28], + // RFC9374 + droneRemoteIdProtocolEntityTags: [new IPv6([0x2001, 0x30, 0, 0, 0, 0, 0, 0]), 28], + // RFC9602 + segmentRouting: [new IPv6([0x5f00, 0, 0, 0, 0, 0, 0, 0]), 16], + reserved: [ + // RFC3849 + [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 23], + // RFC2928 + [new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32], + // RFC9637 + [new IPv6([0x3fff, 0, 0, 0, 0, 0, 0, 0]), 20], + ], + }; - // The 'kind' method exists on both IPv4 and IPv6 classes. - IPv6.prototype.kind = function () { - return 'ipv6'; - }; + // Checks if this address is an IPv4-mapped IPv6 address. + isIPv4MappedAddress() { + return this.range() === 'ipv4Mapped'; + }; - // Checks if this address matches other one within given CIDR range. - IPv6.prototype.match = function (other, cidrRange) { - let ref; + // The 'kind' method exists on both IPv4 and IPv6 classes. + kind() { + return 'ipv6'; + }; - if (cidrRange === undefined) { - ref = other; - other = ref[0]; - cidrRange = ref[1]; - } + // Checks if this address matches other one within given CIDR range. + match(other, cidrRange) { + let ref; - if (other.kind() !== 'ipv6') { - throw new Error('ipaddr: cannot match ipv6 address with non-ipv6 one'); - } + if (cidrRange === undefined) { + ref = other; + other = ref[0]; + cidrRange = ref[1]; + } - return matchCIDR(this.parts, other.parts, 16, cidrRange); - }; + if (other.kind() !== 'ipv6') { + throw new Error('ipaddr: cannot match ipv6 address with non-ipv6 one'); + } - // returns a number of leading ones in IPv6 address, making sure that - // the rest is a solid sequence of 0's (valid netmask) - // returns either the CIDR length or null if mask is not valid - IPv6.prototype.prefixLengthFromSubnetMask = function () { - let cidr = 0; - // non-zero encountered stop scanning for zeroes - let stop = false; - // number of zeroes in octet - const zerotable = { - 0: 16, - 32768: 15, - 49152: 14, - 57344: 13, - 61440: 12, - 63488: 11, - 64512: 10, - 65024: 9, - 65280: 8, - 65408: 7, - 65472: 6, - 65504: 5, - 65520: 4, - 65528: 3, - 65532: 2, - 65534: 1, - 65535: 0 - }; - let part, zeros; - - for (let i = 7; i >= 0; i -= 1) { - part = this.parts[i]; - if (part in zerotable) { - zeros = zerotable[part]; - if (stop && zeros !== 0) { - return null; - } + return matchCIDR(this.parts, other.parts, 16, cidrRange); + }; - if (zeros !== 16) { - stop = true; - } + // returns a number of leading ones in IPv6 address, making sure that + // the rest is a solid sequence of 0's (valid netmask) + // returns either the CIDR length or null if mask is not valid + prefixLengthFromSubnetMask() { + let cidr = 0; + // non-zero encountered stop scanning for zeroes + let stop = false; + // number of zeroes in octet + const zerotable = { + 0: 16, + 32768: 15, + 49152: 14, + 57344: 13, + 61440: 12, + 63488: 11, + 64512: 10, + 65024: 9, + 65280: 8, + 65408: 7, + 65472: 6, + 65504: 5, + 65520: 4, + 65528: 3, + 65532: 2, + 65534: 1, + 65535: 0 + }; + let part, zeros; - cidr += zeros; - } else { + for (let i = 7; i >= 0; i -= 1) { + part = this.parts[i]; + if (part in zerotable) { + zeros = zerotable[part]; + if (stop && zeros !== 0) { return null; } - } - return 128 - cidr; - }; - - - // Checks if the address corresponds to one of the special ranges. - IPv6.prototype.range = function () { - return ipaddr.subnetMatch(this, this.SpecialRanges); - }; + if (zeros !== 16) { + stop = true; + } - // Returns an array of byte-sized values in network order (MSB first) - IPv6.prototype.toByteArray = function () { - let part; - const bytes = []; - const ref = this.parts; - for (let i = 0; i < ref.length; i++) { - part = ref[i]; - bytes.push(part >> 8); - bytes.push(part & 0xff); + cidr += zeros; + } else { + return null; } + } - return bytes; - }; + return 128 - cidr; + }; - // Returns the address in expanded format with all zeroes included, like - // 2001:0db8:0008:0066:0000:0000:0000:0001 - IPv6.prototype.toFixedLengthString = function () { - const addr = ((function () { - const results = []; - for (let i = 0; i < this.parts.length; i++) { - results.push(padPart(this.parts[i].toString(16), 4)); - } - return results; - }).call(this)).join(':'); + // Checks if the address corresponds to one of the special ranges. + range() { + return subnetMatch(this, IPv6.SpecialRanges); + }; + + // Returns an array of byte-sized values in network order (MSB first) + toByteArray() { + let part; + const bytes = []; + const ref = this.parts; + for (let i = 0; i < ref.length; i++) { + part = ref[i]; + bytes.push(part >> 8); + bytes.push(part & 0xff); + } - let suffix = ''; + return bytes; + }; - if (this.zoneId) { - suffix = `%${this.zoneId}`; + // Returns the address in expanded format with all zeroes included, like + // 2001:0db8:0008:0066:0000:0000:0000:0001 + toFixedLengthString() { + const addr = ((function () { + const results = []; + for (let i = 0; i < this.parts.length; i++) { + results.push(padPart(this.parts[i].toString(16), 4)); } - return addr + suffix; - }; + return results; + }).call(this)).join(':'); - // Converts this address to IPv4 address if it is an IPv4-mapped IPv6 address. - // Throws an error otherwise. - IPv6.prototype.toIPv4Address = function () { - if (!this.isIPv4MappedAddress()) { - throw new Error('ipaddr: trying to convert a generic ipv6 address to ipv4'); - } + let suffix = ''; - const ref = this.parts.slice(-2); - const high = ref[0]; - const low = ref[1]; + if (this.zoneId) { + suffix = `%${this.zoneId}`; + } - return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]); - }; + return addr + suffix; + }; - // Returns the address in expanded format with all zeroes included, like - // 2001:db8:8:66:0:0:0:1 - // - // Deprecated: use toFixedLengthString() instead. - IPv6.prototype.toNormalizedString = function () { - const addr = ((function () { - const results = []; + // Converts this address to IPv4 address if it is an IPv4-mapped IPv6 address. + // Throws an error otherwise. + toIPv4Address() { + if (!this.isIPv4MappedAddress()) { + throw new Error('ipaddr: trying to convert a generic ipv6 address to ipv4'); + } - for (let i = 0; i < this.parts.length; i++) { - results.push(this.parts[i].toString(16)); - } + const ref = this.parts.slice(-2); + const high = ref[0]; + const low = ref[1]; - return results; - }).call(this)).join(':'); + return new IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]); + }; - let suffix = ''; + // Returns the address in expanded format with all zeroes included, like + // 2001:db8:8:66:0:0:0:1 + // + // Deprecated: use toFixedLengthString() instead. + toNormalizedString() { + const addr = ((function () { + const results = []; - if (this.zoneId) { - suffix = `%${this.zoneId}`; + for (let i = 0; i < this.parts.length; i++) { + results.push(this.parts[i].toString(16)); } - return addr + suffix; - }; + return results; + }).call(this)).join(':'); - // Returns the address in compact, human-readable format like - // 2001:db8:8:66::1 - // in line with RFC 5952 (see https://tools.ietf.org/html/rfc5952#section-4) - IPv6.prototype.toRFC5952String = function () { - const regex = /((^|:)(0(:|$)){2,})/g; - // The zone identifier (RFC 4007) is not part of the address; match - // against the address alone so a trailing zero run right before the - // "%" suffix still gets compressed (RFC 5952, 4.2.2). - let suffix = ''; - if (this.zoneId) { - suffix = `%${this.zoneId}`; - } - const normalized = this.toNormalizedString(); - const string = normalized.slice(0, normalized.length - suffix.length); - let bestMatchIndex = 0; - let bestMatchLength = -1; - let bestMatchGroups = -1; - let match; - - while ((match = regex.exec(string))) { - // Compare by the number of zero groups, not the matched length: - // a run that is not at the start carries a leading ":" in the - // match, so an equal-length run later in the address would - // otherwise be preferred over the leftmost one (RFC 5952, 4.2.3). - const groups = (match[0].match(/0/g) || []).length; - if (groups > bestMatchGroups) { - bestMatchGroups = groups; - bestMatchIndex = match.index; - bestMatchLength = match[0].length; - } - } + let suffix = ''; - if (bestMatchLength < 0) { - return string + suffix; - } + if (this.zoneId) { + suffix = `%${this.zoneId}`; + } - return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}${suffix}`; - }; + return addr + suffix; + }; - // Returns the address in compact, human-readable format like - // 2001:db8:8:66::1 - // Calls toRFC5952String under the hood. - IPv6.prototype.toString = function () { - return this.toRFC5952String(); - }; + // Returns the address in compact, human-readable format like + // 2001:db8:8:66::1 + // in line with RFC 5952 (see https://tools.ietf.org/html/rfc5952#section-4) + toRFC5952String() { + const regex = /((^|:)(0(:|$)){2,})/g; + // The zone identifier (RFC 4007) is not part of the address; match + // against the address alone so a trailing zero run right before the + // "%" suffix still gets compressed (RFC 5952, 4.2.2). + let suffix = ''; + if (this.zoneId) { + suffix = `%${this.zoneId}`; + } + const normalized = this.toNormalizedString(); + const string = normalized.slice(0, normalized.length - suffix.length); + let bestMatchIndex = 0; + let bestMatchLength = -1; + let bestMatchGroups = -1; + let match; - return IPv6; + while ((match = regex.exec(string))) { + // Compare by the number of zero groups, not the matched length: + // a run that is not at the start carries a leading ":" in the + // match, so an equal-length run later in the address would + // otherwise be preferred over the leftmost one (RFC 5952, 4.2.3). + const groups = (match[0].match(/0/g) || []).length; + if (groups > bestMatchGroups) { + bestMatchGroups = groups; + bestMatchIndex = match.index; + bestMatchLength = match[0].length; + } + } - })(); + if (bestMatchLength < 0) { + return string + suffix; + } + + return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}${suffix}`; + }; + + // Returns the address in compact, human-readable format like + // 2001:db8:8:66::1 + // Calls toRFC5952String under the hood. + toString() { + return this.toRFC5952String(); + }; // A utility function to return broadcast address given the IPv6 interface and prefix length in CIDR notation - ipaddr.IPv6.broadcastAddressFromCIDR = function (string) { + static broadcastAddressFromCIDR = function (string) { try { const cidr = this.parseCIDR(string); const ipInterfaceOctets = cidr[0].toByteArray(); @@ -839,12 +832,12 @@ }; // Checks if a given string is formatted like IPv6 address. - ipaddr.IPv6.isIPv6 = function (string) { + static isIPv6 = function (string) { return this.parser(string) !== null; }; // Checks to see if string is a valid IPv6 Address - ipaddr.IPv6.isValid = function (string) { + static isValid = function (string) { // Since IPv6.isValid is always called first, this shortcut // provides a substantial performance gain. @@ -862,7 +855,7 @@ }; // Checks if a given string is a valid IPv6 address in CIDR notation. - ipaddr.IPv6.isValidCIDR = function (string) { + static isValidCIDR = function (string) { // See note in IPv6.isValid if (typeof string === 'string' && string.indexOf(':') === -1) { @@ -878,7 +871,7 @@ }; // A utility function to return network address given the IPv6 interface and prefix length in CIDR notation - ipaddr.IPv6.networkAddressFromCIDR = function (string) { + static networkAddressFromCIDR = function (string) { let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; try { @@ -901,7 +894,7 @@ // Tries to parse and validate a string with IPv6 address. // Throws an error if it fails. - ipaddr.IPv6.parse = function (string) { + static parse = function (string) { const addr = this.parser(string); if (addr === null) { @@ -911,7 +904,7 @@ return new this(addr.parts, addr.zoneId); }; - ipaddr.IPv6.parseCIDR = function (string) { + static parseCIDR = function (string) { let maskLength, match, parsed; if ((match = string.match(/^(.+)\/(\d+)$/))) { @@ -931,7 +924,7 @@ }; // Parse an IPv6 address. - ipaddr.IPv6.parser = function (string) { + static parser = function (string) { let addr, i, match, octet, octets, zoneId; if ((match = string.match(ipv6Regexes.deprecatedTransitional))) { @@ -974,7 +967,7 @@ }; // A utility function to return subnet mask in IPv6 format given the prefix length - ipaddr.IPv6.subnetMaskFromPrefixLength = function (prefix) { + static subnetMaskFromPrefixLength = function (prefix) { prefix = parseInt(prefix); if (Number.isNaN(prefix) || prefix < 0 || prefix > 128) { throw new Error('ipaddr: invalid IPv6 prefix length'); @@ -995,104 +988,97 @@ return new this(octets); }; +} - // Try to parse an array in network order (MSB first) for IPv4 and IPv6 - ipaddr.fromByteArray = function (bytes) { - const length = bytes.length; - if (length === 4) { - return new ipaddr.IPv4(bytes); - } else if (length === 16) { - return new ipaddr.IPv6(bytes); - } else { - throw new Error('ipaddr: the binary input is neither an IPv6 nor IPv4 address'); - } - }; - - // Checks if the address is valid IP address - ipaddr.isValid = function (string) { - return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string); - }; - - // Checks if the address is valid IP address in CIDR notation - ipaddr.isValidCIDR = function (string) { - return ipaddr.IPv6.isValidCIDR(string) || ipaddr.IPv4.isValidCIDR(string); - }; +// Try to parse an array in network order (MSB first) for IPv4 and IPv6 +export const fromByteArray = function (bytes) { + const length = bytes.length; - - // Attempts to parse an IP Address, first through IPv6 then IPv4. - // Throws an error if it could not be parsed. - ipaddr.parse = function (string) { - if (ipaddr.IPv6.isValid(string)) { - return ipaddr.IPv6.parse(string); - } else if (ipaddr.IPv4.isValid(string)) { - return ipaddr.IPv4.parse(string); - } else { - throw new Error('ipaddr: the address has neither IPv6 nor IPv4 format'); - } - }; - - // Attempt to parse CIDR notation, first through IPv6 then IPv4. - // Throws an error if it could not be parsed. - ipaddr.parseCIDR = function (string) { + if (length === 4) { + return new IPv4(bytes); + } else if (length === 16) { + return new IPv6(bytes); + } else { + throw new Error('ipaddr: the binary input is neither an IPv6 nor IPv4 address'); + } +}; + +// Checks if the address is valid IP address +export const isValid = function (string) { + return IPv6.isValid(string) || IPv4.isValid(string); +}; + +// Checks if the address is valid IP address in CIDR notation +export const isValidCIDR = function (string) { + return IPv6.isValidCIDR(string) || IPv4.isValidCIDR(string); +}; + + +// Attempts to parse an IP Address, first through IPv6 then IPv4. +// Throws an error if it could not be parsed. +export const parse = function (string) { + if (IPv6.isValid(string)) { + return IPv6.parse(string); + } else if (IPv4.isValid(string)) { + return IPv4.parse(string); + } else { + throw new Error('ipaddr: the address has neither IPv6 nor IPv4 format'); + } +}; + +// Attempt to parse CIDR notation, first through IPv6 then IPv4. +// Throws an error if it could not be parsed. +export const parseCIDR = function (string) { + try { + return IPv6.parseCIDR(string); + } catch { try { - return ipaddr.IPv6.parseCIDR(string); - } catch { - try { - return ipaddr.IPv4.parseCIDR(string); - } catch (e) { - throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format', { cause: e }); - } + return IPv4.parseCIDR(string); + } catch (e) { + throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format', { cause: e }); } - }; + } +}; - // Parse an address and return plain IPv4 address if it is an IPv4-mapped address - ipaddr.process = function (string) { - const addr = this.parse(string); +// Parse an address and return plain IPv4 address if it is an IPv4-mapped address +export const process = function (string) { + const addr = parse(string); - if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) { - return addr.toIPv4Address(); - } else { - return addr; - } - }; + if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) { + return addr.toIPv4Address(); + } else { + return addr; + } +}; - // An utility function to ease named range matching. See examples below. - // rangeList can contain both IPv4 and IPv6 subnet entries and will not throw errors - // on matching IPv4 addresses to IPv6 ranges or vice versa. - ipaddr.subnetMatch = function (address, rangeList, defaultName) { - let i, rangeName, rangeSubnets, subnet; +// An utility function to ease named range matching. See examples below. +// rangeList can contain both IPv4 and IPv6 subnet entries and will not throw errors +// on matching IPv4 addresses to IPv6 ranges or vice versa. +export const subnetMatch = function (address, rangeList, defaultName) { + let i, rangeName, rangeSubnets, subnet; - if (defaultName === undefined || defaultName === null) { - defaultName = 'unicast'; - } + if (defaultName === undefined || defaultName === null) { + defaultName = 'unicast'; + } - for (rangeName in rangeList) { - if (Object.prototype.hasOwnProperty.call(rangeList, rangeName)) { - rangeSubnets = rangeList[rangeName]; - // ECMA5 Array.isArray isn't available everywhere - if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) { - rangeSubnets = [rangeSubnets]; - } + for (rangeName in rangeList) { + if (Object.prototype.hasOwnProperty.call(rangeList, rangeName)) { + rangeSubnets = rangeList[rangeName]; + // ECMA5 Array.isArray isn't available everywhere + if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) { + rangeSubnets = [rangeSubnets]; + } - for (i = 0; i < rangeSubnets.length; i++) { - subnet = rangeSubnets[i]; - if (address.kind() === subnet[0].kind() && address.match.apply(address, subnet)) { - return rangeName; - } + for (i = 0; i < rangeSubnets.length; i++) { + subnet = rangeSubnets[i]; + if (address.kind() === subnet[0].kind() && address.match.apply(address, subnet)) { + return rangeName; } } } - - return defaultName; - }; - - // Export for both the CommonJS and browser-like environment - if (typeof module !== 'undefined' && module.exports) { - module.exports = ipaddr; - - } else { - root.ipaddr = ipaddr; } -}(this)); + return defaultName; +}; + diff --git a/package.json b/package.json index 231cd9a..e04badc 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "ipaddr.js", "description": "A library for manipulating IPv4 and IPv6 addresses in JavaScript.", - "type": "commonjs", - "version": "2.5.0", + "type": "module", + "version": "3.0.0", "author": "whitequark ", "directories": { "lib": "./lib" @@ -19,7 +19,8 @@ "test:coverage": "node --test --experimental-test-coverage" }, "files": [ - "lib" + "lib/ipaddr.js", + "lib/ipaddr.d.ts" ], "keywords": [ "ip", @@ -31,8 +32,11 @@ "url": "git+https://github.com/whitequark/ipaddr.js.git" }, "main": "./lib/ipaddr.js", + "exports": { + ".": "./lib/ipaddr.js" + }, "engines": { - "node": ">= 10" + "node": ">= 12" }, "license": "MIT", "types": "./lib/ipaddr.d.ts" diff --git a/test/ipaddr.test.js b/test/ipaddr.test.js index fd724c5..ae12d1b 100644 --- a/test/ipaddr.test.js +++ b/test/ipaddr.test.js @@ -1,276 +1,274 @@ -const assert = require('node:assert') -const { describe, it } = require('node:test') +import { ok, doesNotThrow, throws, equal, deepEqual } from 'node:assert'; +import { describe, it } from 'node:test'; -const { IPv6, IPv4 } = require('../lib/ipaddr'); - -const ipaddr = require('../lib/ipaddr'); +import { IPv4, IPv6, parseCIDR, parse, process, isValid, subnetMatch, fromByteArray } from '../lib/ipaddr.js'; describe('ipaddr', () => { it('should define main classes', () => { - assert.ok(ipaddr.IPv4, 'defines IPv4 class'); - assert.ok(ipaddr.IPv6, 'defines IPv6 class'); + ok(IPv4, 'defines IPv4 class'); + ok(IPv6, 'defines IPv6 class'); }) it('can construct IPv4 from octets', () => { - assert.doesNotThrow(() => { - new ipaddr.IPv4([192, 168, 1, 2]); + doesNotThrow(() => { + new IPv4([192, 168, 1, 2]); }); }) it('refuses to construct invalid IPv4', () => { - assert.throws(() => { - new ipaddr.IPv4([300, 1, 2, 3]); + throws(() => { + new IPv4([300, 1, 2, 3]); }); - assert.throws(() => { - new ipaddr.IPv4([8, 8, 8]); + throws(() => { + new IPv4([8, 8, 8]); }); }) it('converts IPv4 to string correctly', () => { - let addr = new ipaddr.IPv4([192, 168, 1, 1]); - assert.equal(addr.toString(), '192.168.1.1'); - assert.equal(addr.toNormalizedString(), '192.168.1.1'); + let addr = new IPv4([192, 168, 1, 1]); + equal(addr.toString(), '192.168.1.1'); + equal(addr.toNormalizedString(), '192.168.1.1'); }) it('converts IPv4 CIDR to string correctly', () => { - assert.equal(IPv4.parseCIDR('192.168.1.1/24').toString(), '192.168.1.1/24'); + equal(IPv4.parseCIDR('192.168.1.1/24').toString(), '192.168.1.1/24'); }) it('returns correct kind for IPv4', () => { - let addr = new ipaddr.IPv4([1, 2, 3, 4]); - assert.equal(addr.kind(), 'ipv4'); + let addr = new IPv4([1, 2, 3, 4]); + equal(addr.kind(), 'ipv4'); }) it('allows to access IPv4 octets', () => { - let addr = new ipaddr.IPv4([42, 0, 0, 0]); - assert.equal(addr.octets[0], 42); + let addr = new IPv4([42, 0, 0, 0]); + equal(addr.octets[0], 42); }) it('checks IPv4 address format', () => { - assert.equal(ipaddr.IPv4.isIPv4('192.168.007.0xa'), true); - assert.equal(ipaddr.IPv4.isIPv4('1024.0.0.1'), true); - assert.equal(ipaddr.IPv4.isIPv4('8.0xa.wtf.6'), false); + equal(IPv4.isIPv4('192.168.007.0xa'), true); + equal(IPv4.isIPv4('1024.0.0.1'), true); + equal(IPv4.isIPv4('8.0xa.wtf.6'), false); }) it('validates IPv4 addresses', () => { - assert.equal(ipaddr.IPv4.isValid('192.168.007.0xa'), true); - assert.equal(ipaddr.IPv4.isValid('1024.0.0.1'), false); - assert.equal(ipaddr.IPv4.isValid('8.0xa.wtf.6'), false); + equal(IPv4.isValid('192.168.007.0xa'), true); + equal(IPv4.isValid('1024.0.0.1'), false); + equal(IPv4.isValid('8.0xa.wtf.6'), false); }) it('validates IPv4 addresses in CIDR notation', () => { - assert.equal(ipaddr.IPv4.isValidCIDR('192.168.1.1/24'), true); - assert.equal(ipaddr.IPv4.isValidCIDR('10.5.0.1'), false); - assert.equal(ipaddr.IPv4.isValidCIDR('0.0.0.0/-1'), false); - assert.equal(ipaddr.IPv4.isValidCIDR('192.168.1.1/999'), false); + equal(IPv4.isValidCIDR('192.168.1.1/24'), true); + equal(IPv4.isValidCIDR('10.5.0.1'), false); + equal(IPv4.isValidCIDR('0.0.0.0/-1'), false); + equal(IPv4.isValidCIDR('192.168.1.1/999'), false); }) it('parses IPv4 in several weird formats', () => { - assert.deepEqual(ipaddr.IPv4.parse('192.168.1.1').octets, [192, 168, 1, 1]); - assert.deepEqual(ipaddr.IPv4.parse('0xc0.168.1.1').octets, [192, 168, 1, 1]); - assert.deepEqual(ipaddr.IPv4.parse('192.0250.1.1').octets, [192, 168, 1, 1]); - assert.deepEqual(ipaddr.IPv4.parse('0xc0a80101').octets, [192, 168, 1, 1]); - assert.deepEqual(ipaddr.IPv4.parse('030052000401').octets, [192, 168, 1, 1]); - assert.deepEqual(ipaddr.IPv4.parse('3232235777').octets, [192, 168, 1, 1]); - assert.deepEqual(ipaddr.IPv4.parse('127.42.258').octets, [127, 42, 1, 2]); - assert.deepEqual(ipaddr.IPv4.parse('127.66051').octets, [127, 1, 2, 3]); - assert.deepEqual(ipaddr.IPv4.parse('10.1.1.0xff').octets, [10, 1, 1, 255]); + deepEqual(IPv4.parse('192.168.1.1').octets, [192, 168, 1, 1]); + deepEqual(IPv4.parse('0xc0.168.1.1').octets, [192, 168, 1, 1]); + deepEqual(IPv4.parse('192.0250.1.1').octets, [192, 168, 1, 1]); + deepEqual(IPv4.parse('0xc0a80101').octets, [192, 168, 1, 1]); + deepEqual(IPv4.parse('030052000401').octets, [192, 168, 1, 1]); + deepEqual(IPv4.parse('3232235777').octets, [192, 168, 1, 1]); + deepEqual(IPv4.parse('127.42.258').octets, [127, 42, 1, 2]); + deepEqual(IPv4.parse('127.66051').octets, [127, 1, 2, 3]); + deepEqual(IPv4.parse('10.1.1.0xff').octets, [10, 1, 1, 255]); }) it('barfs at invalid IPv4', () => { - assert.throws(() => { - ipaddr.IPv4.parse('10.0.0.wtf'); + throws(() => { + IPv4.parse('10.0.0.wtf'); }); - assert.throws(() => { - ipaddr.IPv4.parse('8.0x1ffffff'); + throws(() => { + IPv4.parse('8.0x1ffffff'); }); - assert.throws(() => { - ipaddr.IPv4.parse('8.8.0x1ffff'); + throws(() => { + IPv4.parse('8.8.0x1ffff'); }); - assert.throws(() => { - ipaddr.IPv4.parse('10.048.1.1'); + throws(() => { + IPv4.parse('10.048.1.1'); }); }) it('matches IPv4 CIDR correctly', () => { - let addr = new ipaddr.IPv4([10, 5, 0, 1]); - assert.equal(addr.match(ipaddr.IPv4.parse('0.0.0.0'), 0), true); - assert.equal(addr.match(ipaddr.IPv4.parse('11.0.0.0'), 8), false); - assert.equal(addr.match(ipaddr.IPv4.parse('10.0.0.0'), 8), true); - assert.equal(addr.match(ipaddr.IPv4.parse('10.0.0.1'), 8), true); - assert.equal(addr.match(ipaddr.IPv4.parse('10.0.0.10'), 8), true); - assert.equal(addr.match(ipaddr.IPv4.parse('10.5.5.0'), 16), true); - assert.equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 16), false); - assert.equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 15), true); - assert.equal(addr.match(ipaddr.IPv4.parse('10.5.0.2'), 32), false); - assert.equal(addr.match(addr, 32), true); + let addr = new IPv4([10, 5, 0, 1]); + equal(addr.match(IPv4.parse('0.0.0.0'), 0), true); + equal(addr.match(IPv4.parse('11.0.0.0'), 8), false); + equal(addr.match(IPv4.parse('10.0.0.0'), 8), true); + equal(addr.match(IPv4.parse('10.0.0.1'), 8), true); + equal(addr.match(IPv4.parse('10.0.0.10'), 8), true); + equal(addr.match(IPv4.parse('10.5.5.0'), 16), true); + equal(addr.match(IPv4.parse('10.4.5.0'), 16), false); + equal(addr.match(IPv4.parse('10.4.5.0'), 15), true); + equal(addr.match(IPv4.parse('10.5.0.2'), 32), false); + equal(addr.match(addr, 32), true); }) it('parses CIDR reversible', () => { - assert.equal(ipaddr.parseCIDR('1.2.3.4/24').toString(), '1.2.3.4/24'); - assert.equal(ipaddr.parseCIDR('::1%zone/24').toString(), '::1%zone/24'); + equal(parseCIDR('1.2.3.4/24').toString(), '1.2.3.4/24'); + equal(parseCIDR('::1%zone/24').toString(), '::1%zone/24'); }) it('parses IPv4 CIDR correctly', () => { - let addr = new ipaddr.IPv4([10, 5, 0, 1]); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('0.0.0.0/0')), true); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('11.0.0.0/8')), false); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.0/8')), true); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.1/8')), true); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.10/8')), true); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.5.0/16')), true); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/16')), false); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/15')), true); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.2/32')), false); - assert.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.1/32')), true); - assert.throws(() => { - ipaddr.IPv4.parseCIDR('10.5.0.1'); + let addr = new IPv4([10, 5, 0, 1]); + equal(addr.match(IPv4.parseCIDR('0.0.0.0/0')), true); + equal(addr.match(IPv4.parseCIDR('11.0.0.0/8')), false); + equal(addr.match(IPv4.parseCIDR('10.0.0.0/8')), true); + equal(addr.match(IPv4.parseCIDR('10.0.0.1/8')), true); + equal(addr.match(IPv4.parseCIDR('10.0.0.10/8')), true); + equal(addr.match(IPv4.parseCIDR('10.5.5.0/16')), true); + equal(addr.match(IPv4.parseCIDR('10.4.5.0/16')), false); + equal(addr.match(IPv4.parseCIDR('10.4.5.0/15')), true); + equal(addr.match(IPv4.parseCIDR('10.5.0.2/32')), false); + equal(addr.match(IPv4.parseCIDR('10.5.0.1/32')), true); + throws(() => { + IPv4.parseCIDR('10.5.0.1'); }); - assert.throws(() => { - ipaddr.IPv4.parseCIDR('0.0.0.0/-1'); + throws(() => { + IPv4.parseCIDR('0.0.0.0/-1'); }); - assert.throws(() => { - ipaddr.IPv4.parseCIDR('0.0.0.0/33'); + throws(() => { + IPv4.parseCIDR('0.0.0.0/33'); }); }) it('detects reserved IPv4 networks', () => { - assert.equal(ipaddr.IPv4.parse('0.0.0.0').range(), 'unspecified'); - assert.equal(ipaddr.IPv4.parse('0.1.0.0').range(), 'unspecified'); - assert.equal(ipaddr.IPv4.parse('10.1.0.1').range(), 'private'); - assert.equal(ipaddr.IPv4.parse('100.64.0.0').range(), 'carrierGradeNat'); - assert.equal(ipaddr.IPv4.parse('100.127.255.255').range(), 'carrierGradeNat'); - assert.equal(ipaddr.IPv4.parse('192.52.193.1').range(), 'amt'); - assert.equal(ipaddr.IPv4.parse('192.168.2.1').range(), 'private'); - assert.equal(ipaddr.IPv4.parse('192.175.48.0').range(), 'as112'); - assert.equal(ipaddr.IPv4.parse('224.100.0.1').range(), 'multicast'); - assert.equal(ipaddr.IPv4.parse('169.254.15.0').range(), 'linkLocal'); - assert.equal(ipaddr.IPv4.parse('127.1.1.1').range(), 'loopback'); - assert.equal(ipaddr.IPv4.parse('255.255.255.255').range(), 'broadcast'); - assert.equal(ipaddr.IPv4.parse('240.1.2.3').range(), 'reserved'); - assert.equal(ipaddr.IPv4.parse('8.8.8.8').range(), 'unicast'); + equal(IPv4.parse('0.0.0.0').range(), 'unspecified'); + equal(IPv4.parse('0.1.0.0').range(), 'unspecified'); + equal(IPv4.parse('10.1.0.1').range(), 'private'); + equal(IPv4.parse('100.64.0.0').range(), 'carrierGradeNat'); + equal(IPv4.parse('100.127.255.255').range(), 'carrierGradeNat'); + equal(IPv4.parse('192.52.193.1').range(), 'amt'); + equal(IPv4.parse('192.168.2.1').range(), 'private'); + equal(IPv4.parse('192.175.48.0').range(), 'as112'); + equal(IPv4.parse('224.100.0.1').range(), 'multicast'); + equal(IPv4.parse('169.254.15.0').range(), 'linkLocal'); + equal(IPv4.parse('127.1.1.1').range(), 'loopback'); + equal(IPv4.parse('255.255.255.255').range(), 'broadcast'); + equal(IPv4.parse('240.1.2.3').range(), 'reserved'); + equal(IPv4.parse('8.8.8.8').range(), 'unicast'); }) it('checks the conventional IPv4 address format', () => { - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('0.0.0.0'), true); - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('127.0.0.1'), true); - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('192.168.1.1'), true); - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('0xc0.168.1.1'), false); + equal(IPv4.isValidFourPartDecimal('0.0.0.0'), true); + equal(IPv4.isValidFourPartDecimal('127.0.0.1'), true); + equal(IPv4.isValidFourPartDecimal('192.168.1.1'), true); + equal(IPv4.isValidFourPartDecimal('0xc0.168.1.1'), false); }) it('checks the conventional IPv4 CIDR format', () => { - assert.equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('192.168.1.1/24'), true); - assert.equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('0.0.0.0/0'), true); - assert.equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('0xc0.168.1.1/24'), false); - assert.equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('192.168.1.1/33'), false); - assert.equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('192.168.1/24'), false); + equal(IPv4.isValidCIDRFourPartDecimal('192.168.1.1/24'), true); + equal(IPv4.isValidCIDRFourPartDecimal('0.0.0.0/0'), true); + equal(IPv4.isValidCIDRFourPartDecimal('0xc0.168.1.1/24'), false); + equal(IPv4.isValidCIDRFourPartDecimal('192.168.1.1/33'), false); + equal(IPv4.isValidCIDRFourPartDecimal('192.168.1/24'), false); }) it('refuses to construct IPv4 address with trailing and leading zeros', () => { - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('000000192.168.100.2'), false); - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('192.0000168.100.2'), false); - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('192.168.100.00000002'), false); - assert.equal(ipaddr.IPv4.isValidFourPartDecimal('192.168.100.20000000'), false); + equal(IPv4.isValidFourPartDecimal('000000192.168.100.2'), false); + equal(IPv4.isValidFourPartDecimal('192.0000168.100.2'), false); + equal(IPv4.isValidFourPartDecimal('192.168.100.00000002'), false); + equal(IPv4.isValidFourPartDecimal('192.168.100.20000000'), false); }) it('can construct IPv6 from 16bit parts', () => { - assert.doesNotThrow(() => { - new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); + doesNotThrow(() => { + new IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); }); }) it('can construct IPv6 from 8bit parts', () => { - assert.doesNotThrow(() => { - new ipaddr.IPv6([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + doesNotThrow(() => { + new IPv6([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); }); - assert.deepEqual( - new ipaddr.IPv6([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), - new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]) + deepEqual( + new IPv6([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), + new IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]) ); }) it('refuses to construct invalid IPv6', () => { - assert.throws(() => { - new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 0, 1]); + throws(() => { + new IPv6([0xfffff, 0, 0, 0, 0, 0, 0, 1]); }); - assert.throws(() => { - new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 1]); + throws(() => { + new IPv6([0xfffff, 0, 0, 0, 0, 0, 1]); }); - assert.throws(() => { - new ipaddr.IPv6([0xffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + throws(() => { + new IPv6([0xffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); }); }) it('converts IPv6 to string correctly', () => { - let addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); - assert.equal(addr.toNormalizedString(), '2001:db8:f53a:0:0:0:0:1'); - assert.equal(addr.toFixedLengthString(), '2001:0db8:f53a:0000:0000:0000:0000:0001'); - assert.equal(addr.toString(), '2001:db8:f53a::1'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 0]).toString(), '::'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toString(), '::1'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toString(), '2001:db8::'); - assert.equal(new ipaddr.IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toString(), '0:ff::'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0xff, 0]).toString(), '::ff:0'); - assert.equal(new ipaddr.IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toString(), '0:0:ff::'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0xff, 0, 0]).toString(), '::ff:0:0'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0xff, 0xff, 0, 0, 0]).toString(), '::ff:ff:0:0:0'); + let addr = new IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); + equal(addr.toNormalizedString(), '2001:db8:f53a:0:0:0:0:1'); + equal(addr.toFixedLengthString(), '2001:0db8:f53a:0000:0000:0000:0000:0001'); + equal(addr.toString(), '2001:db8:f53a::1'); + equal(new IPv6([0, 0, 0, 0, 0, 0, 0, 0]).toString(), '::'); + equal(new IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toString(), '::1'); + equal(new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toString(), '2001:db8::'); + equal(new IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toString(), '0:ff::'); + equal(new IPv6([0, 0, 0, 0, 0, 0, 0xff, 0]).toString(), '::ff:0'); + equal(new IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toString(), '0:0:ff::'); + equal(new IPv6([0, 0, 0, 0, 0, 0xff, 0, 0]).toString(), '::ff:0:0'); + equal(new IPv6([0, 0, 0, 0xff, 0xff, 0, 0, 0]).toString(), '::ff:ff:0:0:0'); // RFC 5952, 4.2.3: on equal-length runs the leftmost is compressed, // including when that run is at the start of the address. - assert.equal(new ipaddr.IPv6([0, 0, 0xff, 0xff, 0, 0, 0xff, 0xff]).toString(), '::ff:ff:0:0:ff:ff'); - assert.equal(new ipaddr.IPv6([0, 0, 1, 0, 0, 1, 0, 0]).toString(), '::1:0:0:1:0:0'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 1, 0, 0, 1]).toString(), '2001:db8::1:0:0:1'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0xdef, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:def:123b:456c:78d'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:0:123b:456c:78d'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc::456c:78d'); + equal(new IPv6([0, 0, 0xff, 0xff, 0, 0, 0xff, 0xff]).toString(), '::ff:ff:0:0:ff:ff'); + equal(new IPv6([0, 0, 1, 0, 0, 1, 0, 0]).toString(), '::1:0:0:1:0:0'); + equal(new IPv6([0x2001, 0xdb8, 0, 0, 1, 0, 0, 1]).toString(), '2001:db8::1:0:0:1'); + equal(new IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0xdef, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:def:123b:456c:78d'); + equal(new IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:0:123b:456c:78d'); + equal(new IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc::456c:78d'); }) it('converts IPv6 CIDR to string correctly', () => { - assert.equal(IPv6.parseCIDR('0:0:0:0:0:0:0:0/64').toString(), '::/64'); - assert.equal(IPv6.parseCIDR('0:0:0:ff:ff:0:0:0/64').toString(), '::ff:ff:0:0:0/64'); - assert.equal(IPv6.parseCIDR('2001:db8:ff:abc:def:123b:456c:78d/64').toString(), '2001:db8:ff:abc:def:123b:456c:78d/64'); + equal(IPv6.parseCIDR('0:0:0:0:0:0:0:0/64').toString(), '::/64'); + equal(IPv6.parseCIDR('0:0:0:ff:ff:0:0:0/64').toString(), '::ff:ff:0:0:0/64'); + equal(IPv6.parseCIDR('2001:db8:ff:abc:def:123b:456c:78d/64').toString(), '2001:db8:ff:abc:def:123b:456c:78d/64'); }) it('converts IPv6 to RFC 5952 string correctly', () => { // see https://tools.ietf.org/html/rfc5952#section-4 - let addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); - assert.equal(addr.toRFC5952String(), '2001:db8:f53a::1'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '::'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toRFC5952String(), '::1'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '2001:db8::'); + let addr = new IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); + equal(addr.toRFC5952String(), '2001:db8:f53a::1'); + equal(new IPv6([0, 0, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '::'); + equal(new IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toRFC5952String(), '::1'); + equal(new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '2001:db8::'); // longest set of zeroes gets collapsed (section 4.2.3) - assert.equal(new ipaddr.IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '0:ff::'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0xff, 0]).toRFC5952String(), '::ff:0'); - assert.equal(new ipaddr.IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toRFC5952String(), '0:0:ff::'); - assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0xff, 0, 0]).toRFC5952String(), '::ff:0:0'); + equal(new IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '0:ff::'); + equal(new IPv6([0, 0, 0, 0, 0, 0, 0xff, 0]).toRFC5952String(), '::ff:0'); + equal(new IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toRFC5952String(), '0:0:ff::'); + equal(new IPv6([0, 0, 0, 0, 0, 0xff, 0, 0]).toRFC5952String(), '::ff:0:0'); - assert.equal(new ipaddr.IPv6([0x2001, 0, 0, 0, 0xff, 0, 0, 0]).toRFC5952String(), '2001::ff:0:0:0'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0xdef, 0x123b, 0x456c, 0x78d]).toRFC5952String(), '2001:db8:ff:abc:def:123b:456c:78d'); + equal(new IPv6([0x2001, 0, 0, 0, 0xff, 0, 0, 0]).toRFC5952String(), '2001::ff:0:0:0'); + equal(new IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0xdef, 0x123b, 0x456c, 0x78d]).toRFC5952String(), '2001:db8:ff:abc:def:123b:456c:78d'); // don't shorten single 0s (section 4.2.2) - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toRFC5952String(), '2001:db8:ff:abc:0:123b:456c:78d'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0x78d, 0x123b, 0x456c, 0]).toRFC5952String(), '2001:db8:ff:abc:78d:123b:456c:0'); - assert.equal(new ipaddr.IPv6([0, 0xdb8, 0xff, 0xabc, 0x78d, 0x123b, 0x456c, 0x2001]).toRFC5952String(), '0:db8:ff:abc:78d:123b:456c:2001'); + equal(new IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toRFC5952String(), '2001:db8:ff:abc:0:123b:456c:78d'); + equal(new IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0x78d, 0x123b, 0x456c, 0]).toRFC5952String(), '2001:db8:ff:abc:78d:123b:456c:0'); + equal(new IPv6([0, 0xdb8, 0xff, 0xabc, 0x78d, 0x123b, 0x456c, 0x2001]).toRFC5952String(), '0:db8:ff:abc:78d:123b:456c:2001'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0, 0x456c, 0x78d]).toRFC5952String(), '2001:db8:ff:abc::456c:78d'); + equal(new IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0, 0x456c, 0x78d]).toRFC5952String(), '2001:db8:ff:abc::456c:78d'); }) it('returns IPv6 zoneIndex', () => { - let addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1], 'utun0'); - assert.equal(addr.toNormalizedString(), '2001:db8:f53a:0:0:0:0:1%utun0'); - assert.equal(addr.toString(), '2001:db8:f53a::1%utun0'); + let addr = new IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1], 'utun0'); + equal(addr.toNormalizedString(), '2001:db8:f53a:0:0:0:0:1%utun0'); + equal(addr.toString(), '2001:db8:f53a::1%utun0'); - assert.equal( - ipaddr.parse('2001:db8:f53a::1%2').toString(), + equal( + parse('2001:db8:f53a::1%2').toString(), '2001:db8:f53a::1%2' ); - assert.equal( - ipaddr.parse('2001:db8:f53a::1%WAT').toString(), + equal( + parse('2001:db8:f53a::1%WAT').toString(), '2001:db8:f53a::1%WAT' ); - assert.equal( - ipaddr.parse('2001:db8:f53a::1%sUp').toString(), + equal( + parse('2001:db8:f53a::1%sUp').toString(), '2001:db8:f53a::1%sUp' ); }) @@ -278,451 +276,451 @@ describe('ipaddr', () => { it('compresses a trailing zero run when a zoneIndex is present', () => { // The zone identifier (RFC 4007) is not part of the address and must // not prevent zero-run compression at the end of it (RFC 5952, 4.2.2). - assert.equal(new ipaddr.IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0], 'eth0').toString(), 'fe80::%eth0'); - assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 'eth0').toRFC5952String(), '2001:db8::%eth0'); - assert.equal(ipaddr.parse('fe80::%eth0').toString(), 'fe80::%eth0'); - assert.equal(ipaddr.parse('::%eth0').toString(), '::%eth0'); + equal(new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0], 'eth0').toString(), 'fe80::%eth0'); + equal(new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 'eth0').toRFC5952String(), '2001:db8::%eth0'); + equal(parse('fe80::%eth0').toString(), 'fe80::%eth0'); + equal(parse('::%eth0').toString(), '::%eth0'); }) it('returns IPv6 zoneIndex for IPv4-mapped IPv6 addresses', () => { - let addr = ipaddr.parse('::ffff:192.168.1.1%eth0'); - assert.equal(addr.toNormalizedString(), '0:0:0:0:0:ffff:c0a8:101%eth0'); - assert.equal(addr.toString(), '::ffff:c0a8:101%eth0'); + let addr = parse('::ffff:192.168.1.1%eth0'); + equal(addr.toNormalizedString(), '0:0:0:0:0:ffff:c0a8:101%eth0'); + equal(addr.toString(), '::ffff:c0a8:101%eth0'); - assert.equal( - ipaddr.parse('::ffff:192.168.1.1%2').toString(), + equal( + parse('::ffff:192.168.1.1%2').toString(), '::ffff:c0a8:101%2' ); - assert.equal( - ipaddr.parse('::ffff:192.168.1.1%WAT').toString(), + equal( + parse('::ffff:192.168.1.1%WAT').toString(), '::ffff:c0a8:101%WAT' ); - assert.equal( - ipaddr.parse('::ffff:192.168.1.1%sUp').toString(), + equal( + parse('::ffff:192.168.1.1%sUp').toString(), '::ffff:c0a8:101%sUp' ); }) it('returns correct kind for IPv6', () => { - let addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); - assert.equal(addr.kind(), 'ipv6'); + let addr = new IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); + equal(addr.kind(), 'ipv6'); }) it('allows to access IPv6 address parts', () => { - let addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 42, 0, 1]); - assert.equal(addr.parts[5], 42); + let addr = new IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 42, 0, 1]); + equal(addr.parts[5], 42); }) it('checks IPv6 address format', () => { - assert.equal(ipaddr.IPv6.isIPv6('2001:db8:F53A::1'), true); - assert.equal(ipaddr.IPv6.isIPv6('200001::1'), true); - assert.equal(ipaddr.IPv6.isIPv6('::ffff:192.168.1.1'), true); - assert.equal(ipaddr.IPv6.isIPv6('::ffff:192.168.1.1%z'), true); - assert.equal(ipaddr.IPv6.isIPv6('::10.2.3.4'), true); - assert.equal(ipaddr.IPv6.isIPv6('::12.34.56.78%z'), true); - assert.equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1'), false); - assert.equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1:0'), false); - assert.equal(ipaddr.IPv6.isIPv6('fe80::wtf'), false); - assert.equal(ipaddr.IPv6.isIPv6('fe80::%'), false); + equal(IPv6.isIPv6('2001:db8:F53A::1'), true); + equal(IPv6.isIPv6('200001::1'), true); + equal(IPv6.isIPv6('::ffff:192.168.1.1'), true); + equal(IPv6.isIPv6('::ffff:192.168.1.1%z'), true); + equal(IPv6.isIPv6('::10.2.3.4'), true); + equal(IPv6.isIPv6('::12.34.56.78%z'), true); + equal(IPv6.isIPv6('::ffff:300.168.1.1'), false); + equal(IPv6.isIPv6('::ffff:300.168.1.1:0'), false); + equal(IPv6.isIPv6('fe80::wtf'), false); + equal(IPv6.isIPv6('fe80::%'), false); }) it('validates IPv6 addresses', () => { - assert.equal(ipaddr.IPv6.isValid('2001:db8:F53A::1'), true); - assert.equal(ipaddr.IPv6.isValid('200001::1'), false); - assert.equal(ipaddr.IPv6.isValid('::ffff:192.168.1.1'), true); - assert.equal(ipaddr.IPv6.isValid('::ffff:192.168.1.1%z'), true); - assert.equal(ipaddr.IPv6.isValid('::1.1.1.1'), true); - assert.equal(ipaddr.IPv6.isValid('::1.2.3.4%z'), true); - assert.equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1'), false); - assert.equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1:0'), false); - assert.equal(ipaddr.IPv6.isValid('::ffff:222.1.41.9000'), false); - assert.equal(ipaddr.IPv6.isValid('2001:db8::F53A::1'), false); - assert.equal(ipaddr.IPv6.isValid('fe80::wtf'), false); - assert.equal(ipaddr.IPv6.isValid('fe80::%'), false); - assert.equal(ipaddr.IPv6.isValid('2002::2:'), false); - assert.equal(ipaddr.IPv6.isValid('::%z'), true); - - assert.equal(ipaddr.IPv6.isValid(undefined), false); + equal(IPv6.isValid('2001:db8:F53A::1'), true); + equal(IPv6.isValid('200001::1'), false); + equal(IPv6.isValid('::ffff:192.168.1.1'), true); + equal(IPv6.isValid('::ffff:192.168.1.1%z'), true); + equal(IPv6.isValid('::1.1.1.1'), true); + equal(IPv6.isValid('::1.2.3.4%z'), true); + equal(IPv6.isValid('::ffff:300.168.1.1'), false); + equal(IPv6.isValid('::ffff:300.168.1.1:0'), false); + equal(IPv6.isValid('::ffff:222.1.41.9000'), false); + equal(IPv6.isValid('2001:db8::F53A::1'), false); + equal(IPv6.isValid('fe80::wtf'), false); + equal(IPv6.isValid('fe80::%'), false); + equal(IPv6.isValid('2002::2:'), false); + equal(IPv6.isValid('::%z'), true); + + equal(IPv6.isValid(undefined), false); }) it('rejects IPv6 parts longer than four hexadecimal digits', () => { - assert.equal(ipaddr.IPv6.isValid('00000::1'), false); - assert.throws(() => ipaddr.IPv6.parse('00000::1')); - assert.equal(ipaddr.IPv6.isValid('00000:0:0:0:0:0:1.2.3.4'), false); - assert.throws(() => ipaddr.IPv6.parse('00000:0:0:0:0:0:1.2.3.4')); + equal(IPv6.isValid('00000::1'), false); + throws(() => IPv6.parse('00000::1')); + equal(IPv6.isValid('00000:0:0:0:0:0:1.2.3.4'), false); + throws(() => IPv6.parse('00000:0:0:0:0:0:1.2.3.4')); }) it('rejects compression of zero IPv6 parts', () => { - assert.equal(ipaddr.IPv6.isValid('1:2:3:4:5:6:7:8::'), false); - assert.throws(() => ipaddr.IPv6.parse('1:2:3:4:5:6:7:8::')); - assert.equal(ipaddr.IPv6.isValid('::1:2:3:4:5:6:7:8'), false); - assert.throws(() => ipaddr.IPv6.parse('::1:2:3:4:5:6:7:8')); + equal(IPv6.isValid('1:2:3:4:5:6:7:8::'), false); + throws(() => IPv6.parse('1:2:3:4:5:6:7:8::')); + equal(IPv6.isValid('::1:2:3:4:5:6:7:8'), false); + throws(() => IPv6.parse('::1:2:3:4:5:6:7:8')); }) it('does not reject compression of exactly one IPv6 part', () => { - assert.equal(ipaddr.IPv6.isValid('1:2:3:4:5:6:7::'), true); - assert.equal(ipaddr.IPv6.isValid('::1:2:3:4:5:6:7'), true); + equal(IPv6.isValid('1:2:3:4:5:6:7::'), true); + equal(IPv6.isValid('::1:2:3:4:5:6:7'), true); }) it('validates IPv6 addresses in CIDR notation', () => { - assert.equal(ipaddr.IPv6.isValidCIDR('::/0'), true); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1%z/64'), true); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/-1'), false); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1'), false); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/129'), false); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1%z/129'), false); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/64%z'), false); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/64%'), false); - assert.equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/64%z/64'), false); - assert.equal(ipaddr.IPv6.isValidCIDR(undefined), false); + equal(IPv6.isValidCIDR('::/0'), true); + equal(IPv6.isValidCIDR('2001:db8:F53A::1%z/64'), true); + equal(IPv6.isValidCIDR('2001:db8:F53A::1/-1'), false); + equal(IPv6.isValidCIDR('2001:db8:F53A::1'), false); + equal(IPv6.isValidCIDR('2001:db8:F53A::1/129'), false); + equal(IPv6.isValidCIDR('2001:db8:F53A::1%z/129'), false); + equal(IPv6.isValidCIDR('2001:db8:F53A::1/64%z'), false); + equal(IPv6.isValidCIDR('2001:db8:F53A::1/64%'), false); + equal(IPv6.isValidCIDR('2001:db8:F53A::1/64%z/64'), false); + equal(IPv6.isValidCIDR(undefined), false); }) it('parses IPv6 in different formats', () => { - assert.deepEqual(ipaddr.IPv6.parse('2001:db8:F53A:0:0:0:0:1').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); - assert.deepEqual(ipaddr.IPv6.parse('fe80::10').parts, [0xfe80, 0, 0, 0, 0, 0, 0, 0x10]); - assert.deepEqual(ipaddr.IPv6.parse('2001:db8:F53A::').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 0]); - assert.deepEqual(ipaddr.IPv6.parse('::1').parts, [0, 0, 0, 0, 0, 0, 0, 1]); - assert.deepEqual(ipaddr.IPv6.parse('::8.8.8.8').parts, [0, 0, 0, 0, 0, 0xffff, 2056, 2056]); - assert.deepEqual(ipaddr.IPv6.parse('FFFF::255.255.255.255').parts, [0xffff, 0, 0, 0, 0, 0, 0xffff, 0xffff]); - assert.deepEqual(ipaddr.IPv6.parse('64:ff9a::0.0.0.0').parts, [0x64, 0xff9a, 0, 0, 0, 0, 0, 0]); - assert.deepEqual(ipaddr.IPv6.parse('::').parts, [0, 0, 0, 0, 0, 0, 0, 0]); - assert.deepEqual(ipaddr.IPv6.parse('::%z').parts, [0, 0, 0, 0, 0, 0, 0, 0]); - assert.deepEqual(ipaddr.IPv6.parse('::%z').zoneId, 'z'); + deepEqual(IPv6.parse('2001:db8:F53A:0:0:0:0:1').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); + deepEqual(IPv6.parse('fe80::10').parts, [0xfe80, 0, 0, 0, 0, 0, 0, 0x10]); + deepEqual(IPv6.parse('2001:db8:F53A::').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 0]); + deepEqual(IPv6.parse('::1').parts, [0, 0, 0, 0, 0, 0, 0, 1]); + deepEqual(IPv6.parse('::8.8.8.8').parts, [0, 0, 0, 0, 0, 0xffff, 2056, 2056]); + deepEqual(IPv6.parse('FFFF::255.255.255.255').parts, [0xffff, 0, 0, 0, 0, 0, 0xffff, 0xffff]); + deepEqual(IPv6.parse('64:ff9a::0.0.0.0').parts, [0x64, 0xff9a, 0, 0, 0, 0, 0, 0]); + deepEqual(IPv6.parse('::').parts, [0, 0, 0, 0, 0, 0, 0, 0]); + deepEqual(IPv6.parse('::%z').parts, [0, 0, 0, 0, 0, 0, 0, 0]); + deepEqual(IPv6.parse('::%z').zoneId, 'z'); }) it('barfs at invalid IPv6', () => { - assert.throws(() => { - ipaddr.IPv6.parse('fe80::0::1'); + throws(() => { + IPv6.parse('fe80::0::1'); }); }) it('throws a descriptive error when parsing a malformed IPv6 address', () => { - assert.throws(() => ipaddr.IPv6.parse('foobar'), /string is not formatted like an IPv6 Address/); - assert.throws(() => ipaddr.IPv6.parse('::ffff:999.1.1.1'), /string is not formatted like an IPv6 Address/); - assert.throws(() => ipaddr.IPv6.parse('1::2::3:1.2.3.4'), /string is not formatted like an IPv6 Address/); + throws(() => IPv6.parse('foobar'), /string is not formatted like an IPv6 Address/); + throws(() => IPv6.parse('::ffff:999.1.1.1'), /string is not formatted like an IPv6 Address/); + throws(() => IPv6.parse('1::2::3:1.2.3.4'), /string is not formatted like an IPv6 Address/); }) it('matches IPv6 CIDR correctly', () => { - let addr = ipaddr.IPv6.parse('2001:db8:f53a::1'); - assert.equal(addr.match(ipaddr.IPv6.parse('::'), 0), true); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53a::1:1'), 64), true); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53b::1:1'), 48), false); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f531::1:1'), 44), true); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f500::1'), 40), true); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f500::1%z'), 40), true); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db9:f500::1'), 40), false); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db9:f500::1'), 40), false); - assert.equal(addr.match(ipaddr.IPv6.parse('2001:db9:f500::1%z'), 40), false); - assert.equal(addr.match(addr, 128), true); + let addr = IPv6.parse('2001:db8:f53a::1'); + equal(addr.match(IPv6.parse('::'), 0), true); + equal(addr.match(IPv6.parse('2001:db8:f53a::1:1'), 64), true); + equal(addr.match(IPv6.parse('2001:db8:f53b::1:1'), 48), false); + equal(addr.match(IPv6.parse('2001:db8:f531::1:1'), 44), true); + equal(addr.match(IPv6.parse('2001:db8:f500::1'), 40), true); + equal(addr.match(IPv6.parse('2001:db8:f500::1%z'), 40), true); + equal(addr.match(IPv6.parse('2001:db9:f500::1'), 40), false); + equal(addr.match(IPv6.parse('2001:db9:f500::1'), 40), false); + equal(addr.match(IPv6.parse('2001:db9:f500::1%z'), 40), false); + equal(addr.match(addr, 128), true); }) it('parses IPv6 CIDR correctly', () => { - let addr = ipaddr.IPv6.parse('2001:db8:f53a::1'); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('::/0')), true); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1:1/64')), true); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53b::1:1/48')), false); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f531::1:1/44')), true); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f500::1/40')), true); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f500::1%z/40')), true); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db9:f500::1/40')), false); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db9:f500::1%z/40')), false); - assert.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/128')), true); - assert.throws(() => { - ipaddr.IPv6.parseCIDR('2001:db8:f53a::1'); + let addr = IPv6.parse('2001:db8:f53a::1'); + equal(addr.match(IPv6.parseCIDR('::/0')), true); + equal(addr.match(IPv6.parseCIDR('2001:db8:f53a::1:1/64')), true); + equal(addr.match(IPv6.parseCIDR('2001:db8:f53b::1:1/48')), false); + equal(addr.match(IPv6.parseCIDR('2001:db8:f531::1:1/44')), true); + equal(addr.match(IPv6.parseCIDR('2001:db8:f500::1/40')), true); + equal(addr.match(IPv6.parseCIDR('2001:db8:f500::1%z/40')), true); + equal(addr.match(IPv6.parseCIDR('2001:db9:f500::1/40')), false); + equal(addr.match(IPv6.parseCIDR('2001:db9:f500::1%z/40')), false); + equal(addr.match(IPv6.parseCIDR('2001:db8:f53a::1/128')), true); + throws(() => { + IPv6.parseCIDR('2001:db8:f53a::1'); }); - assert.throws(() => { - ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/-1'); + throws(() => { + IPv6.parseCIDR('2001:db8:f53a::1/-1'); }); - assert.throws(() => { - ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/129'); + throws(() => { + IPv6.parseCIDR('2001:db8:f53a::1/129'); }); }) it('converts between IPv4-mapped IPv6 addresses and IPv4 addresses', () => { - let addr = ipaddr.IPv4.parse('77.88.21.11'); + let addr = IPv4.parse('77.88.21.11'); let mapped = addr.toIPv4MappedAddress(); - assert.deepEqual(mapped.parts, [0, 0, 0, 0, 0, 0xffff, 0x4d58, 0x150b]); - assert.deepEqual(mapped.toIPv4Address().octets, addr.octets); + deepEqual(mapped.parts, [0, 0, 0, 0, 0, 0xffff, 0x4d58, 0x150b]); + deepEqual(mapped.toIPv4Address().octets, addr.octets); }) it('refuses to convert non-IPv4-mapped IPv6 address to IPv4 address', () => { - assert.throws(() => { - ipaddr.IPv6.parse('2001:db8::1').toIPv4Address(); + throws(() => { + IPv6.parse('2001:db8::1').toIPv4Address(); }); }) it('detects reserved IPv6 networks', () => { - assert.equal(ipaddr.IPv6.parse('::').range(), 'unspecified'); - assert.equal(ipaddr.IPv6.parse('fe80::1234:5678:abcd:0123').range(), 'linkLocal'); - assert.equal(ipaddr.IPv6.parse('ff00::1234').range(), 'multicast'); - assert.equal(ipaddr.IPv6.parse('::1').range(), 'loopback'); - assert.equal(ipaddr.IPv6.parse('100::42').range(), 'discard'); - assert.equal(ipaddr.IPv6.parse('fc00::').range(), 'uniqueLocal'); - assert.equal(ipaddr.IPv6.parse('::ffff:192.168.1.10').range(), 'ipv4Mapped'); - assert.equal(ipaddr.IPv6.parse('fec0::1234').range(), 'deprecatedSiteLocal'); - assert.equal(ipaddr.IPv6.parse('::ffff:0:192.168.1.10').range(), 'rfc6145'); - assert.equal(ipaddr.IPv6.parse('64:ff9b::1234').range(), 'rfc6052'); - assert.equal(ipaddr.IPv6.parse('64:ff9b:1::1234').range(), 'rfc6052'); - assert.equal(ipaddr.IPv6.parse('2002:1f63:45e8::1').range(), '6to4'); - assert.equal(ipaddr.IPv6.parse('2001::4242').range(), 'teredo'); - assert.equal(ipaddr.IPv6.parse('2001:2::').range(), 'benchmarking'); - assert.equal(ipaddr.IPv6.parse('2001:3::').range(), 'amt'); - assert.equal(ipaddr.IPv6.parse('2001:4:112::').range(), 'as112v6'); - assert.equal(ipaddr.IPv6.parse('2620:4f:8000::').range(), 'as112v6'); - assert.equal(ipaddr.IPv6.parse('2001:10::').range(), 'deprecatedOrchid'); - assert.equal(ipaddr.IPv6.parse('2001:20::').range(), 'orchid2'); - assert.equal(ipaddr.IPv6.parse('2001:30::').range(), 'droneRemoteIdProtocolEntityTags'); - assert.equal(ipaddr.IPv6.parse('2001:db8::3210').range(), 'reserved'); - assert.equal(ipaddr.IPv6.parse('2001:470:8:66::1').range(), 'unicast'); - assert.equal(ipaddr.IPv6.parse('2001:470:8:66::1%z').range(), 'unicast'); - assert.equal(ipaddr.IPv6.parse('5f00::1').range(), 'segmentRouting'); - assert.equal(ipaddr.IPv6.parse('3fff::1').range(), 'reserved'); + equal(IPv6.parse('::').range(), 'unspecified'); + equal(IPv6.parse('fe80::1234:5678:abcd:0123').range(), 'linkLocal'); + equal(IPv6.parse('ff00::1234').range(), 'multicast'); + equal(IPv6.parse('::1').range(), 'loopback'); + equal(IPv6.parse('100::42').range(), 'discard'); + equal(IPv6.parse('fc00::').range(), 'uniqueLocal'); + equal(IPv6.parse('::ffff:192.168.1.10').range(), 'ipv4Mapped'); + equal(IPv6.parse('fec0::1234').range(), 'deprecatedSiteLocal'); + equal(IPv6.parse('::ffff:0:192.168.1.10').range(), 'rfc6145'); + equal(IPv6.parse('64:ff9b::1234').range(), 'rfc6052'); + equal(IPv6.parse('64:ff9b:1::1234').range(), 'rfc6052'); + equal(IPv6.parse('2002:1f63:45e8::1').range(), '6to4'); + equal(IPv6.parse('2001::4242').range(), 'teredo'); + equal(IPv6.parse('2001:2::').range(), 'benchmarking'); + equal(IPv6.parse('2001:3::').range(), 'amt'); + equal(IPv6.parse('2001:4:112::').range(), 'as112v6'); + equal(IPv6.parse('2620:4f:8000::').range(), 'as112v6'); + equal(IPv6.parse('2001:10::').range(), 'deprecatedOrchid'); + equal(IPv6.parse('2001:20::').range(), 'orchid2'); + equal(IPv6.parse('2001:30::').range(), 'droneRemoteIdProtocolEntityTags'); + equal(IPv6.parse('2001:db8::3210').range(), 'reserved'); + equal(IPv6.parse('2001:470:8:66::1').range(), 'unicast'); + equal(IPv6.parse('2001:470:8:66::1%z').range(), 'unicast'); + equal(IPv6.parse('5f00::1').range(), 'segmentRouting'); + equal(IPv6.parse('3fff::1').range(), 'reserved'); }) it('is able to determine IP address type', () => { - assert.equal(ipaddr.parse('8.8.8.8').kind(), 'ipv4'); - assert.equal(ipaddr.parse('2001:db8:3312::1').kind(), 'ipv6'); - assert.equal(ipaddr.parse('2001:db8:3312::1%z').kind(), 'ipv6'); + equal(parse('8.8.8.8').kind(), 'ipv4'); + equal(parse('2001:db8:3312::1').kind(), 'ipv6'); + equal(parse('2001:db8:3312::1%z').kind(), 'ipv6'); }) it('throws an error if tried to parse an invalid address', () => { - assert.throws(() => { - ipaddr.parse('::some.nonsense'); + throws(() => { + parse('::some.nonsense'); }); }) it('correctly processes IPv4-mapped addresses', () => { - assert.equal(ipaddr.process('8.8.8.8').kind(), 'ipv4'); - assert.equal(ipaddr.process('2001:db8:3312::1').kind(), 'ipv6'); - assert.equal(ipaddr.process('::ffff:192.168.1.1').kind(), 'ipv4'); - assert.equal(ipaddr.process('::ffff:192.168.1.1%z').kind(), 'ipv4'); - assert.equal(ipaddr.process('::8.8.8.8').kind(), 'ipv4'); + equal(process('8.8.8.8').kind(), 'ipv4'); + equal(process('2001:db8:3312::1').kind(), 'ipv6'); + equal(process('::ffff:192.168.1.1').kind(), 'ipv4'); + equal(process('::ffff:192.168.1.1%z').kind(), 'ipv4'); + equal(process('::8.8.8.8').kind(), 'ipv4'); }) it('correctly converts IPv6 and IPv4 addresses to byte arrays', () => { - assert.deepEqual( - ipaddr.parse('1.2.3.4').toByteArray(), + deepEqual( + parse('1.2.3.4').toByteArray(), [0x1, 0x2, 0x3, 0x4] ); // Fuck yeah. The first byte of Google's IPv6 address is 42. 42! - assert.deepEqual( - ipaddr.parse('2a00:1450:8007::68').toByteArray(), + deepEqual( + parse('2a00:1450:8007::68').toByteArray(), [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68] ); - assert.deepEqual( - ipaddr.parse('2a00:1450:8007::68%z').toByteArray(), + deepEqual( + parse('2a00:1450:8007::68%z').toByteArray(), [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68] ); }) it('correctly parses 1 as an IPv4 address', () => { - assert.equal(ipaddr.IPv6.isValid('1'), false); - assert.equal(ipaddr.IPv4.isValid('1'), true); - assert.deepEqual(new ipaddr.IPv4([0, 0, 0, 1]), ipaddr.parse('1')); + equal(IPv6.isValid('1'), false); + equal(IPv4.isValid('1'), true); + deepEqual(new IPv4([0, 0, 0, 1]), parse('1')); }) it('correctly detects IPv4 and IPv6 CIDR addresses', () => { - assert.deepEqual( - [ipaddr.IPv6.parse('fc00::'), 64], - ipaddr.parseCIDR('fc00::/64') + deepEqual( + [IPv6.parse('fc00::'), 64], + parseCIDR('fc00::/64') ); - assert.deepEqual( - [ipaddr.IPv4.parse('1.2.3.4'), 5], - ipaddr.parseCIDR('1.2.3.4/5') + deepEqual( + [IPv4.parse('1.2.3.4'), 5], + parseCIDR('1.2.3.4/5') ); }) it('does not consider a very large or very small number a valid IP address', () => { - assert.equal(ipaddr.isValid('4999999999'), false); - assert.equal(ipaddr.isValid('-1'), false); + equal(isValid('4999999999'), false); + equal(isValid('-1'), false); }) it('does not hang on ::8:8:8:8:8:8:8:8:8', () => { - assert.equal(ipaddr.IPv6.isValid('::8:8:8:8:8:8:8:8:8'), false); - assert.equal(ipaddr.IPv6.isValid('::8:8:8:8:8:8:8:8:8%z'), false); + equal(IPv6.isValid('::8:8:8:8:8:8:8:8:8'), false); + equal(IPv6.isValid('::8:8:8:8:8:8:8:8:8%z'), false); }) it('subnetMatch does not fail on empty range', () => { - ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 3, 4]), {}, false); - ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 3, 4]), { subnet: [] }, false); + subnetMatch(new IPv4([1, 2, 3, 4]), {}, false); + subnetMatch(new IPv4([1, 2, 3, 4]), { subnet: [] }, false); }) it('subnetMatch returns default subnet on empty range', () => { - assert.equal(ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 3, 4]), {}, false), false); - assert.equal(ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 3, 4]), { subnet: [] }, false), false); + equal(subnetMatch(new IPv4([1, 2, 3, 4]), {}, false), false); + equal(subnetMatch(new IPv4([1, 2, 3, 4]), { subnet: [] }, false), false); }) it('subnetMatch does not fail on IPv4 when looking for IPv6', () => { - let rangelist = { subnet6: ipaddr.parseCIDR('fe80::/64') }; - assert.equal(ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 3, 4]), rangelist, false), false); + let rangelist = { subnet6: parseCIDR('fe80::/64') }; + equal(subnetMatch(new IPv4([1, 2, 3, 4]), rangelist, false), false); }) it('subnetMatch does not fail on IPv6 when looking for IPv4', () => { - let rangelist = { subnet4: ipaddr.parseCIDR('1.2.3.0/24') }; - assert.equal(ipaddr.subnetMatch(new ipaddr.IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 1]), rangelist, false), false); + let rangelist = { subnet4: parseCIDR('1.2.3.0/24') }; + equal(subnetMatch(new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 1]), rangelist, false), false); }) it('subnetMatch can use a hybrid IPv4/IPv6 range list', () => { - let rangelist = { dual64: [ipaddr.parseCIDR('1.2.4.0/24'), ipaddr.parseCIDR('2001:1:2:3::/64')] }; - assert.equal(ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 4, 1]), rangelist, false), 'dual64'); - assert.equal(ipaddr.subnetMatch(new ipaddr.IPv6([0x2001, 1, 2, 3, 0, 0, 0, 1]), rangelist, false), 'dual64'); + let rangelist = { dual64: [parseCIDR('1.2.4.0/24'), parseCIDR('2001:1:2:3::/64')] }; + equal(subnetMatch(new IPv4([1, 2, 4, 1]), rangelist, false), 'dual64'); + equal(subnetMatch(new IPv6([0x2001, 1, 2, 3, 0, 0, 0, 1]), rangelist, false), 'dual64'); }) it('is able to determine IP address type from byte array input', () => { - assert.equal(ipaddr.fromByteArray([0x7f, 0, 0, 1]).kind(), 'ipv4'); - assert.equal(ipaddr.fromByteArray([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]).kind(), 'ipv6'); - assert.throws(() => { - ipaddr.fromByteArray([1]); + equal(fromByteArray([0x7f, 0, 0, 1]).kind(), 'ipv4'); + equal(fromByteArray([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]).kind(), 'ipv6'); + throws(() => { + fromByteArray([1]); }); }) it('prefixLengthFromSubnetMask returns proper CIDR notation for standard IPv4 masks', () => { - assert.equal(ipaddr.IPv4.parse('255.255.255.255').prefixLengthFromSubnetMask(), 32); - assert.equal(ipaddr.IPv4.parse('255.255.255.254').prefixLengthFromSubnetMask(), 31); - assert.equal(ipaddr.IPv4.parse('255.255.255.252').prefixLengthFromSubnetMask(), 30); - assert.equal(ipaddr.IPv4.parse('255.255.255.248').prefixLengthFromSubnetMask(), 29); - assert.equal(ipaddr.IPv4.parse('255.255.255.240').prefixLengthFromSubnetMask(), 28); - assert.equal(ipaddr.IPv4.parse('255.255.255.224').prefixLengthFromSubnetMask(), 27); - assert.equal(ipaddr.IPv4.parse('255.255.255.192').prefixLengthFromSubnetMask(), 26); - assert.equal(ipaddr.IPv4.parse('255.255.255.128').prefixLengthFromSubnetMask(), 25); - assert.equal(ipaddr.IPv4.parse('255.255.255.0').prefixLengthFromSubnetMask(), 24); - assert.equal(ipaddr.IPv4.parse('255.255.254.0').prefixLengthFromSubnetMask(), 23); - assert.equal(ipaddr.IPv4.parse('255.255.252.0').prefixLengthFromSubnetMask(), 22); - assert.equal(ipaddr.IPv4.parse('255.255.248.0').prefixLengthFromSubnetMask(), 21); - assert.equal(ipaddr.IPv4.parse('255.255.240.0').prefixLengthFromSubnetMask(), 20); - assert.equal(ipaddr.IPv4.parse('255.255.224.0').prefixLengthFromSubnetMask(), 19); - assert.equal(ipaddr.IPv4.parse('255.255.192.0').prefixLengthFromSubnetMask(), 18); - assert.equal(ipaddr.IPv4.parse('255.255.128.0').prefixLengthFromSubnetMask(), 17); - assert.equal(ipaddr.IPv4.parse('255.255.0.0').prefixLengthFromSubnetMask(), 16); - assert.equal(ipaddr.IPv4.parse('255.254.0.0').prefixLengthFromSubnetMask(), 15); - assert.equal(ipaddr.IPv4.parse('255.252.0.0').prefixLengthFromSubnetMask(), 14); - assert.equal(ipaddr.IPv4.parse('255.248.0.0').prefixLengthFromSubnetMask(), 13); - assert.equal(ipaddr.IPv4.parse('255.240.0.0').prefixLengthFromSubnetMask(), 12); - assert.equal(ipaddr.IPv4.parse('255.224.0.0').prefixLengthFromSubnetMask(), 11); - assert.equal(ipaddr.IPv4.parse('255.192.0.0').prefixLengthFromSubnetMask(), 10); - assert.equal(ipaddr.IPv4.parse('255.128.0.0').prefixLengthFromSubnetMask(), 9); - assert.equal(ipaddr.IPv4.parse('255.0.0.0').prefixLengthFromSubnetMask(), 8); - assert.equal(ipaddr.IPv4.parse('254.0.0.0').prefixLengthFromSubnetMask(), 7); - assert.equal(ipaddr.IPv4.parse('252.0.0.0').prefixLengthFromSubnetMask(), 6); - assert.equal(ipaddr.IPv4.parse('248.0.0.0').prefixLengthFromSubnetMask(), 5); - assert.equal(ipaddr.IPv4.parse('240.0.0.0').prefixLengthFromSubnetMask(), 4); - assert.equal(ipaddr.IPv4.parse('224.0.0.0').prefixLengthFromSubnetMask(), 3); - assert.equal(ipaddr.IPv4.parse('192.0.0.0').prefixLengthFromSubnetMask(), 2); - assert.equal(ipaddr.IPv4.parse('128.0.0.0').prefixLengthFromSubnetMask(), 1); - assert.equal(ipaddr.IPv4.parse('0.0.0.0').prefixLengthFromSubnetMask(), 0); + equal(IPv4.parse('255.255.255.255').prefixLengthFromSubnetMask(), 32); + equal(IPv4.parse('255.255.255.254').prefixLengthFromSubnetMask(), 31); + equal(IPv4.parse('255.255.255.252').prefixLengthFromSubnetMask(), 30); + equal(IPv4.parse('255.255.255.248').prefixLengthFromSubnetMask(), 29); + equal(IPv4.parse('255.255.255.240').prefixLengthFromSubnetMask(), 28); + equal(IPv4.parse('255.255.255.224').prefixLengthFromSubnetMask(), 27); + equal(IPv4.parse('255.255.255.192').prefixLengthFromSubnetMask(), 26); + equal(IPv4.parse('255.255.255.128').prefixLengthFromSubnetMask(), 25); + equal(IPv4.parse('255.255.255.0').prefixLengthFromSubnetMask(), 24); + equal(IPv4.parse('255.255.254.0').prefixLengthFromSubnetMask(), 23); + equal(IPv4.parse('255.255.252.0').prefixLengthFromSubnetMask(), 22); + equal(IPv4.parse('255.255.248.0').prefixLengthFromSubnetMask(), 21); + equal(IPv4.parse('255.255.240.0').prefixLengthFromSubnetMask(), 20); + equal(IPv4.parse('255.255.224.0').prefixLengthFromSubnetMask(), 19); + equal(IPv4.parse('255.255.192.0').prefixLengthFromSubnetMask(), 18); + equal(IPv4.parse('255.255.128.0').prefixLengthFromSubnetMask(), 17); + equal(IPv4.parse('255.255.0.0').prefixLengthFromSubnetMask(), 16); + equal(IPv4.parse('255.254.0.0').prefixLengthFromSubnetMask(), 15); + equal(IPv4.parse('255.252.0.0').prefixLengthFromSubnetMask(), 14); + equal(IPv4.parse('255.248.0.0').prefixLengthFromSubnetMask(), 13); + equal(IPv4.parse('255.240.0.0').prefixLengthFromSubnetMask(), 12); + equal(IPv4.parse('255.224.0.0').prefixLengthFromSubnetMask(), 11); + equal(IPv4.parse('255.192.0.0').prefixLengthFromSubnetMask(), 10); + equal(IPv4.parse('255.128.0.0').prefixLengthFromSubnetMask(), 9); + equal(IPv4.parse('255.0.0.0').prefixLengthFromSubnetMask(), 8); + equal(IPv4.parse('254.0.0.0').prefixLengthFromSubnetMask(), 7); + equal(IPv4.parse('252.0.0.0').prefixLengthFromSubnetMask(), 6); + equal(IPv4.parse('248.0.0.0').prefixLengthFromSubnetMask(), 5); + equal(IPv4.parse('240.0.0.0').prefixLengthFromSubnetMask(), 4); + equal(IPv4.parse('224.0.0.0').prefixLengthFromSubnetMask(), 3); + equal(IPv4.parse('192.0.0.0').prefixLengthFromSubnetMask(), 2); + equal(IPv4.parse('128.0.0.0').prefixLengthFromSubnetMask(), 1); + equal(IPv4.parse('0.0.0.0').prefixLengthFromSubnetMask(), 0); // negative cases - assert.equal(ipaddr.IPv4.parse('192.168.255.0').prefixLengthFromSubnetMask(), null); - assert.equal(ipaddr.IPv4.parse('255.0.255.0').prefixLengthFromSubnetMask(), null); + equal(IPv4.parse('192.168.255.0').prefixLengthFromSubnetMask(), null); + equal(IPv4.parse('255.0.255.0').prefixLengthFromSubnetMask(), null); }) it('prefixLengthFromSubnetMask returns proper CIDR notation for standard IPv6 masks', () => { - assert.equal(ipaddr.IPv6.parse('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff').prefixLengthFromSubnetMask(), 128); - assert.equal(ipaddr.IPv6.parse('ffff:ffff:ffff:ffff::').prefixLengthFromSubnetMask(), 64); - assert.equal(ipaddr.IPv6.parse('ffff:ffff:ffff:ff80::').prefixLengthFromSubnetMask(), 57); - assert.equal(ipaddr.IPv6.parse('ffff:ffff:ffff::').prefixLengthFromSubnetMask(), 48); - assert.equal(ipaddr.IPv6.parse('ffff:ffff:ffff::%z').prefixLengthFromSubnetMask(), 48); - assert.equal(ipaddr.IPv6.parse('::').prefixLengthFromSubnetMask(), 0); - assert.equal(ipaddr.IPv6.parse('::%z').prefixLengthFromSubnetMask(), 0); + equal(IPv6.parse('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff').prefixLengthFromSubnetMask(), 128); + equal(IPv6.parse('ffff:ffff:ffff:ffff::').prefixLengthFromSubnetMask(), 64); + equal(IPv6.parse('ffff:ffff:ffff:ff80::').prefixLengthFromSubnetMask(), 57); + equal(IPv6.parse('ffff:ffff:ffff::').prefixLengthFromSubnetMask(), 48); + equal(IPv6.parse('ffff:ffff:ffff::%z').prefixLengthFromSubnetMask(), 48); + equal(IPv6.parse('::').prefixLengthFromSubnetMask(), 0); + equal(IPv6.parse('::%z').prefixLengthFromSubnetMask(), 0); // negative cases - assert.equal(ipaddr.IPv6.parse('2001:db8::').prefixLengthFromSubnetMask(), null); - assert.equal(ipaddr.IPv6.parse('ffff:0:0:ffff::').prefixLengthFromSubnetMask(), null); - assert.equal(ipaddr.IPv6.parse('ffff:0:0:ffff::%z').prefixLengthFromSubnetMask(), null); + equal(IPv6.parse('2001:db8::').prefixLengthFromSubnetMask(), null); + equal(IPv6.parse('ffff:0:0:ffff::').prefixLengthFromSubnetMask(), null); + equal(IPv6.parse('ffff:0:0:ffff::%z').prefixLengthFromSubnetMask(), null); }) it('subnetMaskFromPrefixLength returns correct IPv4 subnet mask given prefix length', () => { - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(0), '0.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(1), '128.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(2), '192.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(3), '224.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(4), '240.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(5), '248.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(6), '252.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(7), '254.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(8), '255.0.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(9), '255.128.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(10), '255.192.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(11), '255.224.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(12), '255.240.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(13), '255.248.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(14), '255.252.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(15), '255.254.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(16), '255.255.0.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(17), '255.255.128.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(18), '255.255.192.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(19), '255.255.224.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(20), '255.255.240.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(21), '255.255.248.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(22), '255.255.252.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(23), '255.255.254.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(24), '255.255.255.0'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(25), '255.255.255.128'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(26), '255.255.255.192'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(27), '255.255.255.224'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(28), '255.255.255.240'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(29), '255.255.255.248'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(30), '255.255.255.252'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(31), '255.255.255.254'); - assert.equal(ipaddr.IPv4.subnetMaskFromPrefixLength(32), '255.255.255.255'); + equal(IPv4.subnetMaskFromPrefixLength(0), '0.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(1), '128.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(2), '192.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(3), '224.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(4), '240.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(5), '248.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(6), '252.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(7), '254.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(8), '255.0.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(9), '255.128.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(10), '255.192.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(11), '255.224.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(12), '255.240.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(13), '255.248.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(14), '255.252.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(15), '255.254.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(16), '255.255.0.0'); + equal(IPv4.subnetMaskFromPrefixLength(17), '255.255.128.0'); + equal(IPv4.subnetMaskFromPrefixLength(18), '255.255.192.0'); + equal(IPv4.subnetMaskFromPrefixLength(19), '255.255.224.0'); + equal(IPv4.subnetMaskFromPrefixLength(20), '255.255.240.0'); + equal(IPv4.subnetMaskFromPrefixLength(21), '255.255.248.0'); + equal(IPv4.subnetMaskFromPrefixLength(22), '255.255.252.0'); + equal(IPv4.subnetMaskFromPrefixLength(23), '255.255.254.0'); + equal(IPv4.subnetMaskFromPrefixLength(24), '255.255.255.0'); + equal(IPv4.subnetMaskFromPrefixLength(25), '255.255.255.128'); + equal(IPv4.subnetMaskFromPrefixLength(26), '255.255.255.192'); + equal(IPv4.subnetMaskFromPrefixLength(27), '255.255.255.224'); + equal(IPv4.subnetMaskFromPrefixLength(28), '255.255.255.240'); + equal(IPv4.subnetMaskFromPrefixLength(29), '255.255.255.248'); + equal(IPv4.subnetMaskFromPrefixLength(30), '255.255.255.252'); + equal(IPv4.subnetMaskFromPrefixLength(31), '255.255.255.254'); + equal(IPv4.subnetMaskFromPrefixLength(32), '255.255.255.255'); }) it('subnetMaskFromPrefixLength returns correct IPv6 subnet mask given prefix length', () => { - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(128), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(112), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:0'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(96), 'ffff:ffff:ffff:ffff:ffff:ffff::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(72), 'ffff:ffff:ffff:ffff:ff00::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(64), 'ffff:ffff:ffff:ffff::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(48), 'ffff:ffff:ffff::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(32), 'ffff:ffff::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(16), 'ffff::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(8), 'ff00::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(4), 'f000::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(2), 'c000::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(1), '8000::'); - assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(0), '::'); + equal(IPv6.subnetMaskFromPrefixLength(128), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.subnetMaskFromPrefixLength(112), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:0'); + equal(IPv6.subnetMaskFromPrefixLength(96), 'ffff:ffff:ffff:ffff:ffff:ffff::'); + equal(IPv6.subnetMaskFromPrefixLength(72), 'ffff:ffff:ffff:ffff:ff00::'); + equal(IPv6.subnetMaskFromPrefixLength(64), 'ffff:ffff:ffff:ffff::'); + equal(IPv6.subnetMaskFromPrefixLength(48), 'ffff:ffff:ffff::'); + equal(IPv6.subnetMaskFromPrefixLength(32), 'ffff:ffff::'); + equal(IPv6.subnetMaskFromPrefixLength(16), 'ffff::'); + equal(IPv6.subnetMaskFromPrefixLength(8), 'ff00::'); + equal(IPv6.subnetMaskFromPrefixLength(4), 'f000::'); + equal(IPv6.subnetMaskFromPrefixLength(2), 'c000::'); + equal(IPv6.subnetMaskFromPrefixLength(1), '8000::'); + equal(IPv6.subnetMaskFromPrefixLength(0), '::'); }) it('subnetMaskFromPrefixLength throws on an out-of-range or non-numeric prefix length', () => { - assert.throws(() => ipaddr.IPv4.subnetMaskFromPrefixLength(-1)); - assert.throws(() => ipaddr.IPv4.subnetMaskFromPrefixLength(33)); - assert.throws(() => ipaddr.IPv4.subnetMaskFromPrefixLength('not a number')); - assert.throws(() => ipaddr.IPv6.subnetMaskFromPrefixLength(-1)); - assert.throws(() => ipaddr.IPv6.subnetMaskFromPrefixLength(129)); - assert.throws(() => ipaddr.IPv6.subnetMaskFromPrefixLength('not a number')); + throws(() => IPv4.subnetMaskFromPrefixLength(-1)); + throws(() => IPv4.subnetMaskFromPrefixLength(33)); + throws(() => IPv4.subnetMaskFromPrefixLength('not a number')); + throws(() => IPv6.subnetMaskFromPrefixLength(-1)); + throws(() => IPv6.subnetMaskFromPrefixLength(129)); + throws(() => IPv6.subnetMaskFromPrefixLength('not a number')); }) it('broadcastAddressFromCIDR returns correct IPv4 broadcast address', () => { - assert.equal(ipaddr.IPv4.broadcastAddressFromCIDR('172.0.0.1/24'), '172.0.0.255'); - assert.equal(ipaddr.IPv4.broadcastAddressFromCIDR('172.0.0.1/26'), '172.0.0.63'); + equal(IPv4.broadcastAddressFromCIDR('172.0.0.1/24'), '172.0.0.255'); + equal(IPv4.broadcastAddressFromCIDR('172.0.0.1/26'), '172.0.0.63'); }) it('networkAddressFromCIDR returns correct IPv4 network address', () => { - assert.equal(ipaddr.IPv4.networkAddressFromCIDR('172.0.0.1/24'), '172.0.0.0'); - assert.equal(ipaddr.IPv4.networkAddressFromCIDR('172.0.0.1/5'), '168.0.0.0'); + equal(IPv4.networkAddressFromCIDR('172.0.0.1/24'), '172.0.0.0'); + equal(IPv4.networkAddressFromCIDR('172.0.0.1/5'), '168.0.0.0'); }) it('networkAddressFromCIDR returns correct IPv6 network address', () => { - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('::/0'), '::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f53b::1:1/48'), '2001:db8:f53b::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f531::1:1/44'), '2001:db8:f530::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f500::1/40'), '2001:db8:f500::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f500::1%z/40'), '2001:db8:f500::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db9:f500::1/40'), '2001:db9:f500::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db9:f500::1%z/40'), '2001:db9:f500::'); - assert.equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f53a::1/128'), '2001:db8:f53a::1'); + equal(IPv6.networkAddressFromCIDR('::/0'), '::'); + equal(IPv6.networkAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a::'); + equal(IPv6.networkAddressFromCIDR('2001:db8:f53b::1:1/48'), '2001:db8:f53b::'); + equal(IPv6.networkAddressFromCIDR('2001:db8:f531::1:1/44'), '2001:db8:f530::'); + equal(IPv6.networkAddressFromCIDR('2001:db8:f500::1/40'), '2001:db8:f500::'); + equal(IPv6.networkAddressFromCIDR('2001:db8:f500::1%z/40'), '2001:db8:f500::'); + equal(IPv6.networkAddressFromCIDR('2001:db9:f500::1/40'), '2001:db9:f500::'); + equal(IPv6.networkAddressFromCIDR('2001:db9:f500::1%z/40'), '2001:db9:f500::'); + equal(IPv6.networkAddressFromCIDR('2001:db8:f53a::1/128'), '2001:db8:f53a::1'); }) it('broadcastAddressFromCIDR returns correct IPv6 broadcast address', () => { - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('::/0'), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a:0:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53b::1:1/48'), '2001:db8:f53b:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f531::1:1/44'), '2001:db8:f53f:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f500::1/40'), '2001:db8:f5ff:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f500::1%z/40'), '2001:db8:f5ff:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db9:f500::1/40'), '2001:db9:f5ff:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db9:f500::1%z/40'), '2001:db9:f5ff:ffff:ffff:ffff:ffff:ffff'); - assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1/128'), '2001:db8:f53a::1'); + equal(IPv6.broadcastAddressFromCIDR('::/0'), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a:0:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db8:f53b::1:1/48'), '2001:db8:f53b:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db8:f531::1:1/44'), '2001:db8:f53f:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db8:f500::1/40'), '2001:db8:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db8:f500::1%z/40'), '2001:db8:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db9:f500::1/40'), '2001:db9:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db9:f500::1%z/40'), '2001:db9:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1/128'), '2001:db8:f53a::1'); }) })