diff --git a/Changes.md b/Changes.md index 8b618c0..7cb8bb0 100644 --- a/Changes.md +++ b/Changes.md @@ -1,3 +1,7 @@ +### 3.3.0 - 2026-05-05 + +- ESM-only, dropped CJS support + ### 2.3.0 - 2025-11-27 - add isValidCIDRFourPartDecimal helper for IPv4 CIDR in four-part decimal form diff --git a/README.md b/README.md index 8419d57..60986ed 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,6 @@ and convert between IPv4 and IPv4-mapped IPv6 addresses. `npm install ipaddr.js` -or - -`bower install ipaddr.js` - ## Older Node support Use 2.x release for nodejs versions 10+. diff --git a/bower.json b/bower.json deleted file mode 100644 index 542be6e..0000000 --- a/bower.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "ipaddr.js", - "version": "2.2.0", - "homepage": "https://github.com/whitequark/ipaddr.js", - "authors": [ - "whitequark " - ], - "description": "IP address manipulation library in JavaScript", - "main": "lib/ipaddr.js", - "moduleType": [ - "globals", - "node" - ], - "keywords": [ - "javascript", - "ip", - "address", - "ipv4", - "ipv6" - ], - "license": "MIT", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ] -} diff --git a/ipaddr.min.js b/ipaddr.min.js deleted file mode 100644 index 03da67e..0000000 --- a/ipaddr.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(t){!function(t){"use strict";const r="(0?\\d+|0x[a-f0-9]+)",e={fourOctet:new RegExp(`^${r}\\.${r}\\.${r}\\.${r}$`,"i"),threeOctet:new RegExp(`^${r}\\.${r}\\.${r}$`,"i"),twoOctet:new RegExp(`^${r}\\.${r}$`,"i"),longValue:new RegExp(`^${r}$`,"i")},n=new RegExp("^0[0-7]+$","i"),i=new RegExp("^0x[a-f0-9]+$","i"),o="(?:[0-9a-f]+::?)+",s={zoneIndex:new RegExp("%[0-9a-z]{1,}","i"),native:new RegExp(`^(::)?(${o})?([0-9a-f]+)?(::)?(%[0-9a-z]{1,})?$`,"i"),deprecatedTransitional:new RegExp(`^(?:::)(${r}\\.${r}\\.${r}\\.${r}(%[0-9a-z]{1,})?)$`,"i"),transitional:new RegExp(`^((?:${o})|(?:::)(?:${o})?)${r}\\.${r}\\.${r}\\.${r}(%[0-9a-z]{1,})?$`,"i")};function a(t,r){if(t.indexOf("::")!==t.lastIndexOf("::"))return null;let e,n,i=0,o=-1,a=(t.match(s.zoneIndex)||[])[0];for(a&&(a=a.substring(1),t=t.replace(/%.+$/,""));(o=t.indexOf(":",o+1))>=0;)i++;if("::"===t.substr(0,2)&&i--,"::"===t.substr(-2,2)&&i--,i>r)return null;for(n=r-i,e=":";n--;)e+="0:";return":"===(t=t.replace("::",e))[0]&&(t=t.slice(1)),":"===t[t.length-1]&&(t=t.slice(0,-1)),{parts:r=function(){const r=t.split(":"),e=[];for(let t=0;t0;){if((i=e-n)<0&&(i=0),t[o]>>i!=r[o]>>i)return!1;n-=e,o+=1}return!0}function u(t){if(i.test(t))return parseInt(t,16);if("0"===t[0]&&!isNaN(parseInt(t[1],10))){if(n.test(t))return parseInt(t,8);throw new Error(`ipaddr: cannot parse ${t} as octal`)}return parseInt(t,10)}function d(t,r){for(;t.length=0;n-=1){if(!((i=this.octets[n])in e))return null;if(o=e[i],r&&0!==o)return null;8!==o&&(r=!0),t+=o}return 32-t},t.prototype.range=function(){return c.subnetMatch(this,this.SpecialRanges)},t.prototype.toByteArray=function(){return this.octets.slice(0)},t.prototype.toIPv4MappedAddress=function(){return c.IPv6.parse(`::ffff:${this.toString()}`)},t.prototype.toNormalizedString=function(){return this.toString()},t.prototype.toString=function(){return this.octets.join(".")},t}(),c.IPv4.broadcastAddressFromCIDR=function(t){try{const r=this.parseCIDR(t),e=r[0].toByteArray(),n=this.subnetMaskFromPrefixLength(r[1]).toByteArray(),i=[];let o=0;for(;o<4;)i.push(parseInt(e[o],10)|255^parseInt(n[o],10)),o++;return new this(i)}catch(t){throw new Error("ipaddr: the address does not have IPv4 CIDR format")}},c.IPv4.isIPv4=function(t){return null!==this.parser(t)},c.IPv4.isValid=function(t){try{return new this(this.parser(t)),!0}catch(t){return!1}},c.IPv4.isValidCIDR=function(t){try{return this.parseCIDR(t),!0}catch(t){return!1}},c.IPv4.isValidFourPartDecimal=function(t){return!(!c.IPv4.isValid(t)||!t.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/))},c.IPv4.isValidCIDRFourPartDecimal=function(t){const r=t.match(/^(.+)\/(\d+)$/);return!(!c.IPv4.isValidCIDR(t)||!r)&&c.IPv4.isValidFourPartDecimal(r[1])},c.IPv4.networkAddressFromCIDR=function(t){let r,e,n,i,o;try{for(n=(r=this.parseCIDR(t))[0].toByteArray(),o=this.subnetMaskFromPrefixLength(r[1]).toByteArray(),i=[],e=0;e<4;)i.push(parseInt(n[e],10)&parseInt(o[e],10)),e++;return new this(i)}catch(t){throw new Error("ipaddr: the address does not have IPv4 CIDR format")}},c.IPv4.parse=function(t){const r=this.parser(t);if(null===r)throw new Error("ipaddr: string is not formatted like an IPv4 Address");return new this(r)},c.IPv4.parseCIDR=function(t){let r;if(r=t.match(/^(.+)\/(\d+)$/)){const t=parseInt(r[2]);if(t>=0&&t<=32){const e=[this.parse(r[1]),t];return Object.defineProperty(e,"toString",{value:function(){return this.join("/")}}),e}}throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range")},c.IPv4.parser=function(t){let r,n,i;if(r=t.match(e.fourOctet))return function(){const t=r.slice(1,6),e=[];for(let r=0;r4294967295||i<0)throw new Error("ipaddr: address outside defined range");return function(){const t=[];let r;for(r=0;r<=24;r+=8)t.push(i>>r&255);return t}().reverse()}return(r=t.match(e.twoOctet))?function(){const t=r.slice(1,4),e=[];if((i=u(t[1]))>16777215||i<0)throw new Error("ipaddr: address outside defined range");return e.push(u(t[0])),e.push(i>>16&255),e.push(i>>8&255),e.push(255&i),e}():(r=t.match(e.threeOctet))?function(){const t=r.slice(1,5),e=[];if((i=u(t[2]))>65535||i<0)throw new Error("ipaddr: address outside defined range");return e.push(u(t[0])),e.push(u(t[1])),e.push(i>>8&255),e.push(255&i),e}():null},c.IPv4.subnetMaskFromPrefixLength=function(t){if((t=parseInt(t))<0||t>32)throw new Error("ipaddr: invalid IPv4 prefix length");const r=[0,0,0,0];let e=0;const n=Math.floor(t/8);for(;e=0;o-=1){if(!((n=this.parts[o])in e))return null;if(i=e[n],r&&0!==i)return null;16!==i&&(r=!0),t+=i}return 128-t},t.prototype.range=function(){return c.subnetMatch(this,this.SpecialRanges)},t.prototype.toByteArray=function(){let t;const r=[],e=this.parts;for(let n=0;n>8),r.push(255&t);return r},t.prototype.toFixedLengthString=function(){const t=function(){const t=[];for(let r=0;r>8,255&r,e>>8,255&e])},t.prototype.toNormalizedString=function(){const t=function(){const t=[];for(let r=0;ri&&(n=e.index,i=e[0].length);return i<0?r:`${r.substring(0,n)}::${r.substring(n+i)}`},t.prototype.toString=function(){return this.toRFC5952String()},t}(),c.IPv6.broadcastAddressFromCIDR=function(t){try{const r=this.parseCIDR(t),e=r[0].toByteArray(),n=this.subnetMaskFromPrefixLength(r[1]).toByteArray(),i=[];let o=0;for(;o<16;)i.push(parseInt(e[o],10)|255^parseInt(n[o],10)),o++;return new this(i)}catch(t){throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${t})`)}},c.IPv6.isIPv6=function(t){return null!==this.parser(t)},c.IPv6.isValid=function(t){if("string"==typeof t&&-1===t.indexOf(":"))return!1;try{const r=this.parser(t);return new this(r.parts,r.zoneId),!0}catch(t){return!1}},c.IPv6.isValidCIDR=function(t){if("string"==typeof t&&-1===t.indexOf(":"))return!1;try{return this.parseCIDR(t),!0}catch(t){return!1}},c.IPv6.networkAddressFromCIDR=function(t){let r,e,n,i,o;try{for(n=(r=this.parseCIDR(t))[0].toByteArray(),o=this.subnetMaskFromPrefixLength(r[1]).toByteArray(),i=[],e=0;e<16;)i.push(parseInt(n[e],10)&parseInt(o[e],10)),e++;return new this(i)}catch(t){throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${t})`)}},c.IPv6.parse=function(t){const r=this.parser(t);if(null===r.parts)throw new Error("ipaddr: string is not formatted like an IPv6 Address");return new this(r.parts,r.zoneId)},c.IPv6.parseCIDR=function(t){let r,e,n;if((e=t.match(/^(.+)\/(\d+)$/))&&(r=parseInt(e[2]))>=0&&r<=128)return n=[this.parse(e[1]),r],Object.defineProperty(n,"toString",{value:function(){return this.join("/")}}),n;throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range")},c.IPv6.parser=function(t){let r,e,n,i,o,p;if(n=t.match(s.deprecatedTransitional))return this.parser(`::ffff:${n[1]}`);if(s.native.test(t))return a(t,8);if((n=t.match(s.transitional))&&(p=n[6]||"",r=n[1],n[1].endsWith("::")||(r=r.slice(0,-1)),(r=a(r+p,6)).parts)){for(o=[parseInt(n[2]),parseInt(n[3]),parseInt(n[4]),parseInt(n[5])],e=0;e128)throw new Error("ipaddr: invalid IPv6 prefix length");const r=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];let e=0;const n=Math.floor(t/8);for(;e { + [name: string]: [T, number] | [T, number][]; +} + +// Common methods/properties for IPv4 and IPv6 classes. +export class IP { + prefixLengthFromSubnetMask(): number | null; + toByteArray(): number[]; + toNormalizedString(): string; + toString(): string; +} + +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; + +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[] + + kind(): 'ipv4'; + match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; + range(): IPv4Range; + subnetMatch(rangeList: RangeList, defaultName?: string): string; + toIPv4MappedAddress(): IPv6; +} + +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 + + 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; +} diff --git a/lib/ipaddr.js b/lib/ipaddr.js index 4e3307f..33f530b 100644 --- a/lib/ipaddr.js +++ b/lib/ipaddr.js @@ -1,1067 +1,1065 @@ -(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--; - } + // The following loop would hang if colonCount > parts + if (colonCount > parts) { + return null; + } - if (string.substr(-2, 2) === '::') { - colonCount--; - } + // replacement = ':' + '0:' * (parts - colonCount) + replacementCount = parts - colonCount; + replacement = ':'; + while (replacementCount--) { + replacement += '0:'; + } - // The following loop would hang if colonCount > parts - 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(parseInt(ref[i], 16)); } - parts = (function () { - const ref = string.split(':'); - const results = []; - - for (let i = 0; i < ref.length; i++) { - results.push(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; - - while (cidrBits > 0) { - shift = partSize - cidrBits; - if (shift < 0) { - shift = 0; - } - - if (first[part] >> shift !== second[part] >> shift) { - return false; - } + let part = 0; + let shift; - 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 = {}; - - // 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'); - } +function padPart (part, length) { + while (part.length < length) { + part = `0${part}`; + } - let i, octet; + return part; +} - 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'); - } - } +const ipaddr = {}; - this.octets = octets; +// 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'); } - // 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], - ], - }; - - // The 'kind' method exists on both IPv4 and IPv6 classes. - IPv4.prototype.kind = function () { - return 'ipv4'; - }; - - // 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]; - } + let i, octet; - if (other.kind() !== 'ipv4') { - throw new Error('ipaddr: cannot match ipv4 address with non-ipv4 one'); + 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'); } + } - return matchCIDR(this.octets, other.octets, 8, cidrRange); - }; - - // 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; - } - - if (zeros !== 8) { - stop = true; - } - - cidr += zeros; - } else { - return null; - } - } + this.octets = octets; + } - return 32 - cidr; - }; + // 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], + ], + }; - // Checks if the address corresponds to one of the special ranges. - IPv4.prototype.range = function () { - return ipaddr.subnetMatch(this, this.SpecialRanges); - }; + // The 'kind' method exists on both IPv4 and IPv6 classes. + IPv4.prototype.kind = function () { + return 'ipv4'; + }; - // Returns an array of byte-sized values in network order (MSB first) - IPv4.prototype.toByteArray = function () { - return this.octets.slice(0); - }; + // 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]; + } - // Converts this IPv4 address to an IPv4-mapped IPv6 address. - IPv4.prototype.toIPv4MappedAddress = function () { - return ipaddr.IPv6.parse(`::ffff:${this.toString()}`); - }; + if (other.kind() !== 'ipv4') { + throw new Error('ipaddr: cannot match ipv4 address with non-ipv4 one'); + } - // Symmetrical method strictly for aligning with the IPv6 methods. - IPv4.prototype.toNormalizedString = function () { - return this.toString(); - }; + return matchCIDR(this.octets, other.octets, 8, cidrRange); + }; - // Returns the address in convenient, decimal-dotted format. - IPv4.prototype.toString = function () { - return this.octets.join('.'); + // 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; - return IPv4; - })(); + for (i = 3; i >= 0; i -= 1) { + octet = this.octets[i]; + if (octet in zerotable) { + zeros = zerotable[octet]; + if (stop && zeros !== 0) { + return null; + } - // A utility function to return broadcast address given the IPv4 interface and prefix length in CIDR notation - ipaddr.IPv4.broadcastAddressFromCIDR = function (string) { + if (zeros !== 8) { + stop = true; + } - try { - const cidr = this.parseCIDR(string); - const ipInterfaceOctets = cidr[0].toByteArray(); - const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); - const octets = []; - let i = 0; - while (i < 4) { - // Broadcast address is bitwise OR between ip interface and inverted mask - octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255); - i++; + cidr += zeros; + } else { + return null; } - - return new this(octets); - } catch (e) { - throw new Error('ipaddr: the address does not have IPv4 CIDR format'); } + + return 32 - cidr; }; - // Checks if a given string is formatted like IPv4 address. - ipaddr.IPv4.isIPv4 = function (string) { - return this.parser(string) !== null; + // Checks if the address corresponds to one of the special ranges. + IPv4.prototype.range = function () { + return ipaddr.subnetMatch(this, this.SpecialRanges); }; - // Checks if a given string is a valid IPv4 address. - ipaddr.IPv4.isValid = function (string) { - try { - new this(this.parser(string)); - return true; - } catch (e) { - return false; - } + // Returns an array of byte-sized values in network order (MSB first) + IPv4.prototype.toByteArray = function () { + return this.octets.slice(0); }; - // Checks if a given string is a valid IPv4 address in CIDR notation. - ipaddr.IPv4.isValidCIDR = function (string) { - try { - this.parseCIDR(string); - return true; - } catch (e) { - return false; - } + // Converts this IPv4 address to an IPv4-mapped IPv6 address. + IPv4.prototype.toIPv4MappedAddress = function () { + return ipaddr.IPv6.parse(`::ffff:${this.toString()}`); }; - // 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}$/)) { - return true; - } else { - return false; - } + // Symmetrical method strictly for aligning with the IPv6 methods. + IPv4.prototype.toNormalizedString = function () { + return this.toString(); }; - // Checks if a given string is a full four-part IPv4 Address with CIDR prefix. - ipaddr.IPv4.isValidCIDRFourPartDecimal = function (string) { - const match = string.match(/^(.+)\/(\d+)$/); + // Returns the address in convenient, decimal-dotted format. + IPv4.prototype.toString = function () { + return this.octets.join('.'); + }; - if (!ipaddr.IPv4.isValidCIDR(string) || !match) { - return false; + return IPv4; +})(); + +// A utility function to return broadcast address given the IPv4 interface and prefix length in CIDR notation +ipaddr.IPv4.broadcastAddressFromCIDR = function (string) { + + try { + const cidr = this.parseCIDR(string); + const ipInterfaceOctets = cidr[0].toByteArray(); + const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); + const octets = []; + let i = 0; + while (i < 4) { + // Broadcast address is bitwise OR between ip interface and inverted mask + octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255); + i++; } - return ipaddr.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) { - let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; + return new this(octets); + } catch (e) { + throw new Error('ipaddr: the address does not have IPv4 CIDR format'); + } +}; - try { - cidr = this.parseCIDR(string); - ipInterfaceOctets = cidr[0].toByteArray(); - subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); - octets = []; - i = 0; - while (i < 4) { - // Network address is bitwise AND between ip interface and mask - octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10)); - i++; - } +// Checks if a given string is formatted like IPv4 address. +ipaddr.IPv4.isIPv4 = function (string) { + return this.parser(string) !== null; +}; - return new this(octets); - } catch (e) { - throw new Error('ipaddr: the address does not have IPv4 CIDR format'); - } - }; +// Checks if a given string is a valid IPv4 address. +ipaddr.IPv4.isValid = function (string) { + try { + new this(this.parser(string)); + return true; + } catch (e) { + return false; + } +}; - // Tries to parse and validate a string with IPv4 address. - // Throws an error if it fails. - ipaddr.IPv4.parse = function (string) { - const parts = this.parser(string); +// Checks if a given string is a valid IPv4 address in CIDR notation. +ipaddr.IPv4.isValidCIDR = function (string) { + try { + this.parseCIDR(string); + return true; + } catch (e) { + return false; + } +}; - if (parts === null) { - throw new Error('ipaddr: string is not formatted like an IPv4 Address'); - } +// 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}$/)) { + return true; + } else { + return false; + } +}; - return new this(parts); - }; +// Checks if a given string is a full four-part IPv4 Address with CIDR prefix. +ipaddr.IPv4.isValidCIDRFourPartDecimal = function (string) { + const match = string.match(/^(.+)\/(\d+)$/); - // Parses the string as an IPv4 Address with CIDR Notation. - ipaddr.IPv4.parseCIDR = function (string) { - let match; + if (!ipaddr.IPv4.isValidCIDR(string) || !match) { + return false; + } - if ((match = string.match(/^(.+)\/(\d+)$/))) { - const maskLength = parseInt(match[2]); - if (maskLength >= 0 && maskLength <= 32) { - const parsed = [this.parse(match[1]), maskLength]; - Object.defineProperty(parsed, 'toString', { - value: function () { - return this.join('/'); - } - }); - return parsed; - } + return ipaddr.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) { + let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; + + try { + cidr = this.parseCIDR(string); + ipInterfaceOctets = cidr[0].toByteArray(); + subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); + octets = []; + i = 0; + while (i < 4) { + // Network address is bitwise AND between ip interface and mask + octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10)); + i++; } - throw new Error('ipaddr: string is not formatted like an IPv4 CIDR range'); - }; + return new this(octets); + } catch (e) { + throw new Error('ipaddr: the address does not have IPv4 CIDR format'); + } +}; - // 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) { - let match, part, value; - - // parseInt recognizes all that octal & hexadecimal weirdness for us - if ((match = string.match(ipv4Regexes.fourOctet))) { - return (function () { - const ref = match.slice(1, 6); - const results = []; - - for (let i = 0; i < ref.length; i++) { - part = ref[i]; - results.push(parseIntAuto(part)); - } +// Tries to parse and validate a string with IPv4 address. +// Throws an error if it fails. +ipaddr.IPv4.parse = function (string) { + const parts = this.parser(string); - return results; - })(); - } else if ((match = string.match(ipv4Regexes.longValue))) { - value = parseIntAuto(match[1]); - if (value > 0xffffffff || value < 0) { - throw new Error('ipaddr: address outside defined range'); - } + if (parts === null) { + throw new Error('ipaddr: string is not formatted like an IPv4 Address'); + } + + return new this(parts); +}; - return ((function () { - const results = []; - let shift; +// Parses the string as an IPv4 Address with CIDR Notation. +ipaddr.IPv4.parseCIDR = function (string) { + let match; - for (shift = 0; shift <= 24; shift += 8) { - results.push((value >> shift) & 0xff); + if ((match = string.match(/^(.+)\/(\d+)$/))) { + const maskLength = parseInt(match[2]); + if (maskLength >= 0 && maskLength <= 32) { + const parsed = [this.parse(match[1]), maskLength]; + Object.defineProperty(parsed, 'toString', { + value: function () { + return this.join('/'); } + }); + return parsed; + } + } - return results; - })()).reverse(); - } else if ((match = string.match(ipv4Regexes.twoOctet))) { - return (function () { - const ref = match.slice(1, 4); - const results = []; + throw new Error('ipaddr: string is not formatted like an IPv4 CIDR range'); +}; - value = parseIntAuto(ref[1]); - if (value > 0xffffff || value < 0) { - throw new Error('ipaddr: address outside defined range'); - } +// 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) { + let match, part, value; - results.push(parseIntAuto(ref[0])); - results.push((value >> 16) & 0xff); - results.push((value >> 8) & 0xff); - results.push( value & 0xff); - - return results; - })(); - } else if ((match = string.match(ipv4Regexes.threeOctet))) { - return (function () { - const ref = match.slice(1, 5); - const results = []; - - value = parseIntAuto(ref[2]); - if (value > 0xffff || value < 0) { - throw new Error('ipaddr: address outside defined range'); - } + // parseInt recognizes all that octal & hexadecimal weirdness for us + if ((match = string.match(ipv4Regexes.fourOctet))) { + return (function () { + const ref = match.slice(1, 6); + const results = []; - results.push(parseIntAuto(ref[0])); - results.push(parseIntAuto(ref[1])); - results.push((value >> 8) & 0xff); - results.push( value & 0xff); + for (let i = 0; i < ref.length; i++) { + part = ref[i]; + results.push(parseIntAuto(part)); + } - return results; - })(); - } else { - return null; + return results; + })(); + } else if ((match = string.match(ipv4Regexes.longValue))) { + value = parseIntAuto(match[1]); + if (value > 0xffffffff || value < 0) { + throw new Error('ipaddr: address outside defined range'); } - }; - // A utility function to return subnet mask in IPv4 format given the prefix length - ipaddr.IPv4.subnetMaskFromPrefixLength = function (prefix) { - prefix = parseInt(prefix); - if (prefix < 0 || prefix > 32) { - throw new Error('ipaddr: invalid IPv4 prefix length'); - } + return ((function () { + const results = []; + let shift; - const octets = [0, 0, 0, 0]; - let j = 0; - const filledOctetCount = Math.floor(prefix / 8); + for (shift = 0; shift <= 24; shift += 8) { + results.push((value >> shift) & 0xff); + } - while (j < filledOctetCount) { - octets[j] = 255; - j++; - } + return results; + })()).reverse(); + } else if ((match = string.match(ipv4Regexes.twoOctet))) { + return (function () { + const ref = match.slice(1, 4); + const results = []; - if (filledOctetCount < 4) { - octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8); - } + value = parseIntAuto(ref[1]); + if (value > 0xffffff || value < 0) { + throw new Error('ipaddr: address outside defined range'); + } - return new this(octets); - }; + results.push(parseIntAuto(ref[0])); + results.push((value >> 16) & 0xff); + results.push((value >> 8) & 0xff); + results.push( value & 0xff); - // 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'); - } + return results; + })(); + } else if ((match = string.match(ipv4Regexes.threeOctet))) { + return (function () { + const ref = match.slice(1, 5); + const results = []; - 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'); - } + value = parseIntAuto(ref[2]); + if (value > 0xffff || value < 0) { + throw new Error('ipaddr: address outside defined range'); } - if (zoneId) { - this.zoneId = zoneId; - } - } + results.push(parseIntAuto(ref[0])); + results.push(parseIntAuto(ref[1])); + results.push((value >> 8) & 0xff); + results.push( value & 0xff); - // 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], - // 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], - // 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: [ - [new IPv6([0x2001, 0x4, 0x112, 0, 0, 0, 0, 0]), 48], - [new IPv6([0x2620, 0x4f, 0x8000, 0, 0, 0, 0, 0]), 48], - ], - deprecated: [new IPv6([0x2001, 0x10, 0, 0, 0, 0, 0, 0]), 28], - orchid2: [new IPv6([0x2001, 0x20, 0, 0, 0, 0, 0, 0]), 28], - droneRemoteIdProtocolEntityTags: [new IPv6([0x2001, 0x30, 0, 0, 0, 0, 0, 0]), 28], - 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], - ], - }; + return results; + })(); + } else { + return null; + } +}; - // Checks if this address is an IPv4-mapped IPv6 address. - IPv6.prototype.isIPv4MappedAddress = function () { - return this.range() === 'ipv4Mapped'; - }; +// A utility function to return subnet mask in IPv4 format given the prefix length +ipaddr.IPv4.subnetMaskFromPrefixLength = function (prefix) { + prefix = parseInt(prefix); + if (prefix < 0 || prefix > 32) { + throw new Error('ipaddr: invalid IPv4 prefix length'); + } - // The 'kind' method exists on both IPv4 and IPv6 classes. - IPv6.prototype.kind = function () { - return 'ipv6'; - }; + const octets = [0, 0, 0, 0]; + let j = 0; + const filledOctetCount = Math.floor(prefix / 8); - // Checks if this address matches other one within given CIDR range. - IPv6.prototype.match = function (other, cidrRange) { - let ref; + while (j < filledOctetCount) { + octets[j] = 255; + j++; + } + + if (filledOctetCount < 4) { + octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8); + } - if (cidrRange === undefined) { - ref = other; - other = ref[0]; - cidrRange = ref[1]; + 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'); + } - if (other.kind() !== 'ipv6') { - throw new Error('ipaddr: cannot match ipv6 address with non-ipv6 one'); + 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'); } + } - return matchCIDR(this.parts, other.parts, 16, cidrRange); - }; + if (zoneId) { + this.zoneId = zoneId; + } + } - // 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; - } - - if (zeros !== 16) { - stop = true; - } - - cidr += zeros; - } else { - return null; - } - } + // 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], + // 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], + // 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: [ + [new IPv6([0x2001, 0x4, 0x112, 0, 0, 0, 0, 0]), 48], + [new IPv6([0x2620, 0x4f, 0x8000, 0, 0, 0, 0, 0]), 48], + ], + deprecated: [new IPv6([0x2001, 0x10, 0, 0, 0, 0, 0, 0]), 28], + orchid2: [new IPv6([0x2001, 0x20, 0, 0, 0, 0, 0, 0]), 28], + droneRemoteIdProtocolEntityTags: [new IPv6([0x2001, 0x30, 0, 0, 0, 0, 0, 0]), 28], + 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], + ], + }; - return 128 - cidr; - }; + // Checks if this address is an IPv4-mapped IPv6 address. + IPv6.prototype.isIPv4MappedAddress = function () { + return this.range() === 'ipv4Mapped'; + }; + // The 'kind' method exists on both IPv4 and IPv6 classes. + IPv6.prototype.kind = function () { + return 'ipv6'; + }; - // Checks if the address corresponds to one of the special ranges. - IPv6.prototype.range = function () { - return ipaddr.subnetMatch(this, this.SpecialRanges); - }; + // Checks if this address matches other one within given CIDR range. + IPv6.prototype.match = function (other, cidrRange) { + let ref; - // 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); - } + if (cidrRange === undefined) { + ref = other; + other = ref[0]; + cidrRange = ref[1]; + } + + if (other.kind() !== 'ipv6') { + throw new Error('ipaddr: cannot match ipv6 address with non-ipv6 one'); + } - return bytes; + return matchCIDR(this.parts, other.parts, 16, cidrRange); + }; + + // 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; - // 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)); + 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 results; - }).call(this)).join(':'); - - let suffix = ''; + if (zeros !== 16) { + stop = true; + } - if (this.zoneId) { - suffix = `%${this.zoneId}`; + cidr += zeros; + } else { + return null; } + } - return addr + suffix; - }; + return 128 - cidr; + }; - // 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'); - } - const ref = this.parts.slice(-2); - const high = ref[0]; - const low = ref[1]; + // Checks if the address corresponds to one of the special ranges. + IPv6.prototype.range = function () { + return ipaddr.subnetMatch(this, this.SpecialRanges); + }; - return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]); - }; + // 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); + } - // 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 = []; + return bytes; + }; - for (let i = 0; i < this.parts.length; i++) { - results.push(this.parts[i].toString(16)); - } + // 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(':'); + return results; + }).call(this)).join(':'); - let suffix = ''; + let suffix = ''; - if (this.zoneId) { - suffix = `%${this.zoneId}`; - } + if (this.zoneId) { + suffix = `%${this.zoneId}`; + } - return addr + suffix; - }; + return addr + suffix; + }; - // 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; - const string = this.toNormalizedString(); - let bestMatchIndex = 0; - let bestMatchLength = -1; - let match; - - while ((match = regex.exec(string))) { - if (match[0].length > bestMatchLength) { - bestMatchIndex = match.index; - bestMatchLength = match[0].length; - } - } + // 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'); + } - if (bestMatchLength < 0) { - return string; - } + const ref = this.parts.slice(-2); + const high = ref[0]; + const low = ref[1]; - return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}`; - }; + return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]); + }; - // 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 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 = []; - return IPv6; + for (let i = 0; i < this.parts.length; i++) { + results.push(this.parts[i].toString(16)); + } - })(); + return results; + }).call(this)).join(':'); - // A utility function to return broadcast address given the IPv6 interface and prefix length in CIDR notation - ipaddr.IPv6.broadcastAddressFromCIDR = function (string) { - try { - const cidr = this.parseCIDR(string); - const ipInterfaceOctets = cidr[0].toByteArray(); - const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); - const octets = []; - let i = 0; - while (i < 16) { - // Broadcast address is bitwise OR between ip interface and inverted mask - octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255); - i++; - } + let suffix = ''; - return new this(octets); - } catch (e) { - throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); + if (this.zoneId) { + suffix = `%${this.zoneId}`; } - }; - // Checks if a given string is formatted like IPv6 address. - ipaddr.IPv6.isIPv6 = function (string) { - return this.parser(string) !== null; + return addr + suffix; }; - // Checks to see if string is a valid IPv6 Address - ipaddr.IPv6.isValid = function (string) { + // 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; + const string = this.toNormalizedString(); + let bestMatchIndex = 0; + let bestMatchLength = -1; + let match; - // Since IPv6.isValid is always called first, this shortcut - // provides a substantial performance gain. - if (typeof string === 'string' && string.indexOf(':') === -1) { - return false; + while ((match = regex.exec(string))) { + if (match[0].length > bestMatchLength) { + bestMatchIndex = match.index; + bestMatchLength = match[0].length; + } } - try { - const addr = this.parser(string); - new this(addr.parts, addr.zoneId); - return true; - } catch (e) { - return false; + if (bestMatchLength < 0) { + return string; } + + return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}`; }; - // Checks if a given string is a valid IPv6 address in CIDR notation. - ipaddr.IPv6.isValidCIDR = function (string) { + // 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(); + }; - // See note in IPv6.isValid - if (typeof string === 'string' && string.indexOf(':') === -1) { - return false; + return IPv6; + +})(); + +// A utility function to return broadcast address given the IPv6 interface and prefix length in CIDR notation +ipaddr.IPv6.broadcastAddressFromCIDR = function (string) { + try { + const cidr = this.parseCIDR(string); + const ipInterfaceOctets = cidr[0].toByteArray(); + const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); + const octets = []; + let i = 0; + while (i < 16) { + // Broadcast address is bitwise OR between ip interface and inverted mask + octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255); + i++; } - try { - this.parseCIDR(string); - return true; - } catch (e) { - return false; - } - }; + return new this(octets); + } catch (e) { + throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); + } +}; - // A utility function to return network address given the IPv6 interface and prefix length in CIDR notation - ipaddr.IPv6.networkAddressFromCIDR = function (string) { - let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; +// Checks if a given string is formatted like IPv6 address. +ipaddr.IPv6.isIPv6 = function (string) { + return this.parser(string) !== null; +}; - try { - cidr = this.parseCIDR(string); - ipInterfaceOctets = cidr[0].toByteArray(); - subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); - octets = []; - i = 0; - while (i < 16) { - // Network address is bitwise AND between ip interface and mask - octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10)); - i++; - } +// Checks to see if string is a valid IPv6 Address +ipaddr.IPv6.isValid = function (string) { - return new this(octets); - } catch (e) { - throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); - } - }; + // Since IPv6.isValid is always called first, this shortcut + // provides a substantial performance gain. + if (typeof string === 'string' && string.indexOf(':') === -1) { + return false; + } - // Tries to parse and validate a string with IPv6 address. - // Throws an error if it fails. - ipaddr.IPv6.parse = function (string) { + try { const addr = this.parser(string); + new this(addr.parts, addr.zoneId); + return true; + } catch (e) { + return false; + } +}; - if (addr.parts === null) { - throw new Error('ipaddr: string is not formatted like an IPv6 Address'); - } +// Checks if a given string is a valid IPv6 address in CIDR notation. +ipaddr.IPv6.isValidCIDR = function (string) { - return new this(addr.parts, addr.zoneId); - }; + // See note in IPv6.isValid + if (typeof string === 'string' && string.indexOf(':') === -1) { + return false; + } - ipaddr.IPv6.parseCIDR = function (string) { - let maskLength, match, parsed; - - if ((match = string.match(/^(.+)\/(\d+)$/))) { - maskLength = parseInt(match[2]); - if (maskLength >= 0 && maskLength <= 128) { - parsed = [this.parse(match[1]), maskLength]; - Object.defineProperty(parsed, 'toString', { - value: function () { - return this.join('/'); - } - }); - return parsed; - } + try { + this.parseCIDR(string); + return true; + } catch (e) { + return false; + } +}; + +// A utility function to return network address given the IPv6 interface and prefix length in CIDR notation +ipaddr.IPv6.networkAddressFromCIDR = function (string) { + let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; + + try { + cidr = this.parseCIDR(string); + ipInterfaceOctets = cidr[0].toByteArray(); + subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); + octets = []; + i = 0; + while (i < 16) { + // Network address is bitwise AND between ip interface and mask + octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10)); + i++; } - throw new Error('ipaddr: string is not formatted like an IPv6 CIDR range'); - }; + return new this(octets); + } catch (e) { + throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); + } +}; - // Parse an IPv6 address. - ipaddr.IPv6.parser = function (string) { - let addr, i, match, octet, octets, zoneId; +// Tries to parse and validate a string with IPv6 address. +// Throws an error if it fails. +ipaddr.IPv6.parse = function (string) { + const addr = this.parser(string); - if ((match = string.match(ipv6Regexes.deprecatedTransitional))) { - return this.parser(`::ffff:${match[1]}`); - } - if (ipv6Regexes.native.test(string)) { - return expandIPv6(string, 8); - } - if ((match = string.match(ipv6Regexes.transitional))) { - zoneId = match[6] || ''; - addr = match[1] - if (!match[1].endsWith('::')) { - addr = addr.slice(0, -1) - } - addr = expandIPv6(addr + zoneId, 6); - if (addr.parts) { - octets = [ - parseInt(match[2]), - parseInt(match[3]), - parseInt(match[4]), - parseInt(match[5]) - ]; - for (i = 0; i < octets.length; i++) { - octet = octets[i]; - if (!((0 <= octet && octet <= 255))) { - return null; - } - } + if (addr.parts === null) { + throw new Error('ipaddr: string is not formatted like an IPv6 Address'); + } - addr.parts.push(octets[0] << 8 | octets[1]); - addr.parts.push(octets[2] << 8 | octets[3]); - return { - parts: addr.parts, - zoneId: addr.zoneId - }; - } - } + return new this(addr.parts, addr.zoneId); +}; - return null; - }; +ipaddr.IPv6.parseCIDR = function (string) { + let maskLength, match, parsed; - // A utility function to return subnet mask in IPv6 format given the prefix length - ipaddr.IPv6.subnetMaskFromPrefixLength = function (prefix) { - prefix = parseInt(prefix); - if (prefix < 0 || prefix > 128) { - throw new Error('ipaddr: invalid IPv6 prefix length'); + if ((match = string.match(/^(.+)\/(\d+)$/))) { + maskLength = parseInt(match[2]); + if (maskLength >= 0 && maskLength <= 128) { + parsed = [this.parse(match[1]), maskLength]; + Object.defineProperty(parsed, 'toString', { + value: function () { + return this.join('/'); + } + }); + return parsed; } + } - const octets = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - let j = 0; - const filledOctetCount = Math.floor(prefix / 8); + throw new Error('ipaddr: string is not formatted like an IPv6 CIDR range'); +}; - while (j < filledOctetCount) { - octets[j] = 255; - j++; +// Parse an IPv6 address. +ipaddr.IPv6.parser = function (string) { + let addr, i, match, octet, octets, zoneId; + + if ((match = string.match(ipv6Regexes.deprecatedTransitional))) { + return this.parser(`::ffff:${match[1]}`); + } + if (ipv6Regexes.native.test(string)) { + return expandIPv6(string, 8); + } + if ((match = string.match(ipv6Regexes.transitional))) { + zoneId = match[6] || ''; + addr = match[1] + if (!match[1].endsWith('::')) { + addr = addr.slice(0, -1) } + addr = expandIPv6(addr + zoneId, 6); + if (addr.parts) { + octets = [ + parseInt(match[2]), + parseInt(match[3]), + parseInt(match[4]), + parseInt(match[5]) + ]; + for (i = 0; i < octets.length; i++) { + octet = octets[i]; + if (!((0 <= octet && octet <= 255))) { + return null; + } + } - if (filledOctetCount < 16) { - octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8); + addr.parts.push(octets[0] << 8 | octets[1]); + addr.parts.push(octets[2] << 8 | octets[3]); + return { + parts: addr.parts, + zoneId: addr.zoneId + }; } + } - return new this(octets); - }; + return null; +}; - // Try to parse an array in network order (MSB first) for IPv4 and IPv6 - ipaddr.fromByteArray = function (bytes) { - const length = bytes.length; +// A utility function to return subnet mask in IPv6 format given the prefix length +ipaddr.IPv6.subnetMaskFromPrefixLength = function (prefix) { + prefix = parseInt(prefix); + if (prefix < 0 || prefix > 128) { + throw new Error('ipaddr: invalid IPv6 prefix 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'); - } - }; + const octets = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + let j = 0; + const filledOctetCount = Math.floor(prefix / 8); - // Checks if the address is valid IP address - ipaddr.isValid = function (string) { - return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string); - }; + while (j < filledOctetCount) { + octets[j] = 255; + j++; + } - // Checks if the address is valid IP address in CIDR notation - ipaddr.isValidCIDR = function (string) { - return ipaddr.IPv6.isValidCIDR(string) || ipaddr.IPv4.isValidCIDR(string); - }; + if (filledOctetCount < 16) { + octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8); + } + return new this(octets); +}; - // 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'); - } - }; +// Try to parse an array in network order (MSB first) for IPv4 and IPv6 +ipaddr.fromByteArray = function (bytes) { + const length = bytes.length; - // 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 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); +}; + + +// 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) { + try { + return ipaddr.IPv6.parseCIDR(string); + } catch (e) { try { - return ipaddr.IPv6.parseCIDR(string); - } catch (e) { - try { - return ipaddr.IPv4.parseCIDR(string); - } catch (e2) { - throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format'); - } + return ipaddr.IPv4.parseCIDR(string); + } catch (e2) { + throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format'); } - }; + } +}; - // 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 +ipaddr.process = function (string) { + const addr = ipaddr.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. +ipaddr.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; +}; + +export const IPv4 = ipaddr.IPv4; +export const IPv6 = ipaddr.IPv6; +export const fromByteArray = ipaddr.fromByteArray; +export const isValid = ipaddr.isValid; +export const parse = ipaddr.parse; +export const parseCIDR = ipaddr.parseCIDR; +export const process = ipaddr.process; +export const subnetMatch = ipaddr.subnetMatch; +export const isValidCIDR = ipaddr.isValidCIDR; \ No newline at end of file diff --git a/lib/ipaddr.js.d.ts b/lib/ipaddr.js.d.ts deleted file mode 100644 index ee02c02..0000000 --- a/lib/ipaddr.js.d.ts +++ /dev/null @@ -1,72 +0,0 @@ -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'; - - 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; - } - - 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; - - 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[] - - kind(): 'ipv4'; - match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; - range(): IPv4Range; - subnetMatch(rangeList: RangeList, defaultName?: string): string; - toIPv4MappedAddress(): IPv6; - } - - 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 - - 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 = Address; -} diff --git a/package.json b/package.json index 8f1daf4..e9d9ba6 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,19 @@ { "name": "ipaddr.js", "description": "A library for manipulating IPv4 and IPv6 addresses in JavaScript.", - "version": "2.3.0", + "version": "3.3.0", "author": "whitequark ", "directories": { "lib": "./lib" }, - "dependencies": {}, "devDependencies": { - "eslint": "^9.19.0", - "uglify-es": "*" + "esbuild": "^0.28.0", + "eslint": "^9.19.0" }, "scripts": { - "lint": "npx eslint lib", - "lintfix": "npx eslint --fix lib test", - "build": "npx uglifyjs --compress --mangle --wrap=window -o ipaddr.min.js lib/ipaddr.js", + "lint": "eslint lib", + "lintfix": "eslint --fix lib test", + "build": "esbuild lib/ipaddr.js --bundle --minify --sourcemap --outfile=ipaddr.min.js", "test": "node --test" }, "files": [ @@ -26,11 +25,21 @@ "ipv4", "ipv6" ], - "repository": "git://github.com/whitequark/ipaddr.js", + "repository": { + "type": "git", + "url": "git+https://github.com/whitequark/ipaddr.js.git" + }, + "exports": { + ".": { + "types": "./lib/ipaddr.d.ts", + "import": "./lib/ipaddr.js" + } + }, "main": "./lib/ipaddr.js", + "types": "./lib/ipaddr.d.ts", "engines": { "node": ">= 10" }, "license": "MIT", - "types": "./lib/ipaddr.js.d.ts" + "type": "module" } diff --git a/test/ipaddr.test.js b/test/ipaddr.test.js index e83f7a7..95f2f0b 100644 --- a/test/ipaddr.test.js +++ b/test/ipaddr.test.js @@ -1,270 +1,270 @@ -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'); +import { IPv6, IPv4 } from '../lib/ipaddr.js'; -const ipaddr = require('../lib/ipaddr'); +import ipaddr 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(ipaddr.IPv4, 'defines IPv4 class'); + ok(ipaddr.IPv6, 'defines IPv6 class'); }) it('can construct IPv4 from octets', () => { - assert.doesNotThrow(() => { + doesNotThrow(() => { new ipaddr.IPv4([192, 168, 1, 2]); }); }) it('refuses to construct invalid IPv4', () => { - assert.throws(() => { + throws(() => { new ipaddr.IPv4([300, 1, 2, 3]); }); - assert.throws(() => { + throws(() => { new ipaddr.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'); + 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'); + 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); + 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(ipaddr.IPv4.isIPv4('192.168.007.0xa'), true); + equal(ipaddr.IPv4.isIPv4('1024.0.0.1'), true); + equal(ipaddr.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(ipaddr.IPv4.isValid('192.168.007.0xa'), true); + equal(ipaddr.IPv4.isValid('1024.0.0.1'), false); + equal(ipaddr.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(ipaddr.IPv4.isValidCIDR('192.168.1.1/24'), true); + equal(ipaddr.IPv4.isValidCIDR('10.5.0.1'), false); + equal(ipaddr.IPv4.isValidCIDR('0.0.0.0/-1'), false); + equal(ipaddr.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(ipaddr.IPv4.parse('192.168.1.1').octets, [192, 168, 1, 1]); + deepEqual(ipaddr.IPv4.parse('0xc0.168.1.1').octets, [192, 168, 1, 1]); + deepEqual(ipaddr.IPv4.parse('192.0250.1.1').octets, [192, 168, 1, 1]); + deepEqual(ipaddr.IPv4.parse('0xc0a80101').octets, [192, 168, 1, 1]); + deepEqual(ipaddr.IPv4.parse('030052000401').octets, [192, 168, 1, 1]); + deepEqual(ipaddr.IPv4.parse('3232235777').octets, [192, 168, 1, 1]); + deepEqual(ipaddr.IPv4.parse('127.42.258').octets, [127, 42, 1, 2]); + deepEqual(ipaddr.IPv4.parse('127.66051').octets, [127, 1, 2, 3]); + deepEqual(ipaddr.IPv4.parse('10.1.1.0xff').octets, [10, 1, 1, 255]); }) it('barfs at invalid IPv4', () => { - assert.throws(() => { + throws(() => { ipaddr.IPv4.parse('10.0.0.wtf'); }); - assert.throws(() => { + throws(() => { ipaddr.IPv4.parse('8.0x1ffffff'); }); - assert.throws(() => { + throws(() => { ipaddr.IPv4.parse('8.8.0x1ffff'); }); - assert.throws(() => { + throws(() => { ipaddr.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); + equal(addr.match(ipaddr.IPv4.parse('0.0.0.0'), 0), true); + equal(addr.match(ipaddr.IPv4.parse('11.0.0.0'), 8), false); + equal(addr.match(ipaddr.IPv4.parse('10.0.0.0'), 8), true); + equal(addr.match(ipaddr.IPv4.parse('10.0.0.1'), 8), true); + equal(addr.match(ipaddr.IPv4.parse('10.0.0.10'), 8), true); + equal(addr.match(ipaddr.IPv4.parse('10.5.5.0'), 16), true); + equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 16), false); + equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 15), true); + equal(addr.match(ipaddr.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(ipaddr.parseCIDR('1.2.3.4/24').toString(), '1.2.3.4/24'); + equal(ipaddr.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(() => { + equal(addr.match(ipaddr.IPv4.parseCIDR('0.0.0.0/0')), true); + equal(addr.match(ipaddr.IPv4.parseCIDR('11.0.0.0/8')), false); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.0/8')), true); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.1/8')), true); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.10/8')), true); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.5.0/16')), true); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/16')), false); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/15')), true); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.2/32')), false); + equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.1/32')), true); + throws(() => { ipaddr.IPv4.parseCIDR('10.5.0.1'); }); - assert.throws(() => { + throws(() => { ipaddr.IPv4.parseCIDR('0.0.0.0/-1'); }); - assert.throws(() => { + throws(() => { ipaddr.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(ipaddr.IPv4.parse('0.0.0.0').range(), 'unspecified'); + equal(ipaddr.IPv4.parse('0.1.0.0').range(), 'unspecified'); + equal(ipaddr.IPv4.parse('10.1.0.1').range(), 'private'); + equal(ipaddr.IPv4.parse('100.64.0.0').range(), 'carrierGradeNat'); + equal(ipaddr.IPv4.parse('100.127.255.255').range(), 'carrierGradeNat'); + equal(ipaddr.IPv4.parse('192.52.193.1').range(), 'amt'); + equal(ipaddr.IPv4.parse('192.168.2.1').range(), 'private'); + equal(ipaddr.IPv4.parse('192.175.48.0').range(), 'as112'); + equal(ipaddr.IPv4.parse('224.100.0.1').range(), 'multicast'); + equal(ipaddr.IPv4.parse('169.254.15.0').range(), 'linkLocal'); + equal(ipaddr.IPv4.parse('127.1.1.1').range(), 'loopback'); + equal(ipaddr.IPv4.parse('255.255.255.255').range(), 'broadcast'); + equal(ipaddr.IPv4.parse('240.1.2.3').range(), 'reserved'); + equal(ipaddr.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(ipaddr.IPv4.isValidFourPartDecimal('0.0.0.0'), true); + equal(ipaddr.IPv4.isValidFourPartDecimal('127.0.0.1'), true); + equal(ipaddr.IPv4.isValidFourPartDecimal('192.168.1.1'), true); + equal(ipaddr.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(ipaddr.IPv4.isValidCIDRFourPartDecimal('192.168.1.1/24'), true); + equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('0.0.0.0/0'), true); + equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('0xc0.168.1.1/24'), false); + equal(ipaddr.IPv4.isValidCIDRFourPartDecimal('192.168.1.1/33'), false); + equal(ipaddr.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(ipaddr.IPv4.isValidFourPartDecimal('000000192.168.100.2'), false); + equal(ipaddr.IPv4.isValidFourPartDecimal('192.0000168.100.2'), false); + equal(ipaddr.IPv4.isValidFourPartDecimal('192.168.100.00000002'), false); + equal(ipaddr.IPv4.isValidFourPartDecimal('192.168.100.20000000'), false); }) it('can construct IPv6 from 16bit parts', () => { - assert.doesNotThrow(() => { + doesNotThrow(() => { new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); }); }) it('can construct IPv6 from 8bit parts', () => { - assert.doesNotThrow(() => { + doesNotThrow(() => { new ipaddr.IPv6([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); }); - assert.deepEqual( + 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]) ); }) it('refuses to construct invalid IPv6', () => { - assert.throws(() => { + throws(() => { new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 0, 1]); }); - assert.throws(() => { + throws(() => { new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 1]); }); - assert.throws(() => { + throws(() => { new ipaddr.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'); - 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(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 ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 0]).toString(), '::'); + equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toString(), '::1'); + equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toString(), '2001:db8::'); + equal(new ipaddr.IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toString(), '0:ff::'); + equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0xff, 0]).toString(), '::ff:0'); + equal(new ipaddr.IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toString(), '0:0:ff::'); + equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0xff, 0, 0]).toString(), '::ff:0:0'); + equal(new ipaddr.IPv6([0, 0, 0, 0xff, 0xff, 0, 0, 0]).toString(), '::ff:ff:0:0:0'); + equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0xdef, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:def:123b:456c:78d'); + equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:0:123b:456c:78d'); + equal(new ipaddr.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::'); + equal(addr.toRFC5952String(), '2001:db8:f53a::1'); + equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '::'); + equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toRFC5952String(), '::1'); + equal(new ipaddr.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 ipaddr.IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toRFC5952String(), '0:ff::'); + equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0xff, 0]).toRFC5952String(), '::ff:0'); + equal(new ipaddr.IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toRFC5952String(), '0:0:ff::'); + equal(new ipaddr.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 ipaddr.IPv6([0x2001, 0, 0, 0, 0xff, 0, 0, 0]).toRFC5952String(), '2001::ff:0:0:0'); + equal(new ipaddr.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 ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toRFC5952String(), '2001:db8:ff:abc:0:123b:456c:78d'); + equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0x78d, 0x123b, 0x456c, 0]).toRFC5952String(), '2001:db8:ff:abc:78d:123b:456c:0'); + equal(new ipaddr.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 ipaddr.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'); + equal(addr.toNormalizedString(), '2001:db8:f53a:0:0:0:0:1%utun0'); + equal(addr.toString(), '2001:db8:f53a::1%utun0'); - assert.equal( + equal( ipaddr.parse('2001:db8:f53a::1%2').toString(), '2001:db8:f53a::1%2' ); - assert.equal( + equal( ipaddr.parse('2001:db8:f53a::1%WAT').toString(), '2001:db8:f53a::1%WAT' ); - assert.equal( + equal( ipaddr.parse('2001:db8:f53a::1%sUp').toString(), '2001:db8:f53a::1%sUp' ); @@ -272,18 +272,18 @@ describe('ipaddr', () => { 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'); + equal(addr.toNormalizedString(), '0:0:0:0:0:ffff:c0a8:101%eth0'); + equal(addr.toString(), '::ffff:c0a8:101%eth0'); - assert.equal( + equal( ipaddr.parse('::ffff:192.168.1.1%2').toString(), '::ffff:c0a8:101%2' ); - assert.equal( + equal( ipaddr.parse('::ffff:192.168.1.1%WAT').toString(), '::ffff:c0a8:101%WAT' ); - assert.equal( + equal( ipaddr.parse('::ffff:192.168.1.1%sUp').toString(), '::ffff:c0a8:101%sUp' ); @@ -291,110 +291,110 @@ describe('ipaddr', () => { it('returns correct kind for IPv6', () => { let addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); - assert.equal(addr.kind(), 'ipv6'); + 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); + 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(ipaddr.IPv6.isIPv6('2001:db8:F53A::1'), true); + equal(ipaddr.IPv6.isIPv6('200001::1'), true); + equal(ipaddr.IPv6.isIPv6('::ffff:192.168.1.1'), true); + equal(ipaddr.IPv6.isIPv6('::ffff:192.168.1.1%z'), true); + equal(ipaddr.IPv6.isIPv6('::10.2.3.4'), true); + equal(ipaddr.IPv6.isIPv6('::12.34.56.78%z'), true); + equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1'), false); + equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1:0'), false); + equal(ipaddr.IPv6.isIPv6('fe80::wtf'), false); + equal(ipaddr.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(ipaddr.IPv6.isValid('2001:db8:F53A::1'), true); + equal(ipaddr.IPv6.isValid('200001::1'), false); + equal(ipaddr.IPv6.isValid('::ffff:192.168.1.1'), true); + equal(ipaddr.IPv6.isValid('::ffff:192.168.1.1%z'), true); + equal(ipaddr.IPv6.isValid('::1.1.1.1'), true); + equal(ipaddr.IPv6.isValid('::1.2.3.4%z'), true); + equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1'), false); + equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1:0'), false); + equal(ipaddr.IPv6.isValid('::ffff:222.1.41.9000'), false); + equal(ipaddr.IPv6.isValid('2001:db8::F53A::1'), false); + equal(ipaddr.IPv6.isValid('fe80::wtf'), false); + equal(ipaddr.IPv6.isValid('fe80::%'), false); + equal(ipaddr.IPv6.isValid('2002::2:'), false); + equal(ipaddr.IPv6.isValid('::%z'), true); + + equal(ipaddr.IPv6.isValid(undefined), false); }) 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(ipaddr.IPv6.isValidCIDR('::/0'), true); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1%z/64'), true); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/-1'), false); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1'), false); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/129'), false); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1%z/129'), false); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/64%z'), false); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/64%'), false); + equal(ipaddr.IPv6.isValidCIDR('2001:db8:F53A::1/64%z/64'), false); + equal(ipaddr.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(ipaddr.IPv6.parse('2001:db8:F53A:0:0:0:0:1').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]); + deepEqual(ipaddr.IPv6.parse('fe80::10').parts, [0xfe80, 0, 0, 0, 0, 0, 0, 0x10]); + deepEqual(ipaddr.IPv6.parse('2001:db8:F53A::').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 0]); + deepEqual(ipaddr.IPv6.parse('::1').parts, [0, 0, 0, 0, 0, 0, 0, 1]); + deepEqual(ipaddr.IPv6.parse('::8.8.8.8').parts, [0, 0, 0, 0, 0, 0xffff, 2056, 2056]); + deepEqual(ipaddr.IPv6.parse('FFFF::255.255.255.255').parts, [0xffff, 0, 0, 0, 0, 0, 0xffff, 0xffff]); + deepEqual(ipaddr.IPv6.parse('64:ff9a::0.0.0.0').parts, [0x64, 0xff9a, 0, 0, 0, 0, 0, 0]); + deepEqual(ipaddr.IPv6.parse('::').parts, [0, 0, 0, 0, 0, 0, 0, 0]); + deepEqual(ipaddr.IPv6.parse('::%z').parts, [0, 0, 0, 0, 0, 0, 0, 0]); + deepEqual(ipaddr.IPv6.parse('::%z').zoneId, 'z'); }) it('barfs at invalid IPv6', () => { - assert.throws(() => { + throws(() => { ipaddr.IPv6.parse('fe80::0::1'); }); }) 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); + equal(addr.match(ipaddr.IPv6.parse('::'), 0), true); + equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53a::1:1'), 64), true); + equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53b::1:1'), 48), false); + equal(addr.match(ipaddr.IPv6.parse('2001:db8:f531::1:1'), 44), true); + equal(addr.match(ipaddr.IPv6.parse('2001:db8:f500::1'), 40), true); + equal(addr.match(ipaddr.IPv6.parse('2001:db8:f500::1%z'), 40), true); + equal(addr.match(ipaddr.IPv6.parse('2001:db9:f500::1'), 40), false); + equal(addr.match(ipaddr.IPv6.parse('2001:db9:f500::1'), 40), false); + equal(addr.match(ipaddr.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(() => { + equal(addr.match(ipaddr.IPv6.parseCIDR('::/0')), true); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1:1/64')), true); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53b::1:1/48')), false); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f531::1:1/44')), true); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f500::1/40')), true); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f500::1%z/40')), true); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db9:f500::1/40')), false); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db9:f500::1%z/40')), false); + equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/128')), true); + throws(() => { ipaddr.IPv6.parseCIDR('2001:db8:f53a::1'); }); - assert.throws(() => { + throws(() => { ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/-1'); }); - assert.throws(() => { + throws(() => { ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/129'); }); }) @@ -402,101 +402,101 @@ describe('ipaddr', () => { it('converts between IPv4-mapped IPv6 addresses and IPv4 addresses', () => { let addr = ipaddr.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(() => { + throws(() => { ipaddr.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('::ffff:0:192.168.1.10').range(), 'rfc6145'); - assert.equal(ipaddr.IPv6.parse('64:ff9b::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(), 'deprecated'); - 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'); + equal(ipaddr.IPv6.parse('::').range(), 'unspecified'); + equal(ipaddr.IPv6.parse('fe80::1234:5678:abcd:0123').range(), 'linkLocal'); + equal(ipaddr.IPv6.parse('ff00::1234').range(), 'multicast'); + equal(ipaddr.IPv6.parse('::1').range(), 'loopback'); + equal(ipaddr.IPv6.parse('100::42').range(), 'discard'); + equal(ipaddr.IPv6.parse('fc00::').range(), 'uniqueLocal'); + equal(ipaddr.IPv6.parse('::ffff:192.168.1.10').range(), 'ipv4Mapped'); + equal(ipaddr.IPv6.parse('::ffff:0:192.168.1.10').range(), 'rfc6145'); + equal(ipaddr.IPv6.parse('64:ff9b::1234').range(), 'rfc6052'); + equal(ipaddr.IPv6.parse('2002:1f63:45e8::1').range(), '6to4'); + equal(ipaddr.IPv6.parse('2001::4242').range(), 'teredo'); + equal(ipaddr.IPv6.parse('2001:2::').range(), 'benchmarking'); + equal(ipaddr.IPv6.parse('2001:3::').range(), 'amt'); + equal(ipaddr.IPv6.parse('2001:4:112::').range(), 'as112v6'); + equal(ipaddr.IPv6.parse('2620:4f:8000::').range(), 'as112v6'); + equal(ipaddr.IPv6.parse('2001:10::').range(), 'deprecated'); + equal(ipaddr.IPv6.parse('2001:20::').range(), 'orchid2'); + equal(ipaddr.IPv6.parse('2001:30::').range(), 'droneRemoteIdProtocolEntityTags'); + equal(ipaddr.IPv6.parse('2001:db8::3210').range(), 'reserved'); + equal(ipaddr.IPv6.parse('2001:470:8:66::1').range(), 'unicast'); + equal(ipaddr.IPv6.parse('2001:470:8:66::1%z').range(), 'unicast'); }) 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(ipaddr.parse('8.8.8.8').kind(), 'ipv4'); + equal(ipaddr.parse('2001:db8:3312::1').kind(), 'ipv6'); + equal(ipaddr.parse('2001:db8:3312::1%z').kind(), 'ipv6'); }) it('throws an error if tried to parse an invalid address', () => { - assert.throws(() => { + throws(() => { ipaddr.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(ipaddr.process('8.8.8.8').kind(), 'ipv4'); + equal(ipaddr.process('2001:db8:3312::1').kind(), 'ipv6'); + equal(ipaddr.process('::ffff:192.168.1.1').kind(), 'ipv4'); + equal(ipaddr.process('::ffff:192.168.1.1%z').kind(), 'ipv4'); + equal(ipaddr.process('::8.8.8.8').kind(), 'ipv4'); }) it('correctly converts IPv6 and IPv4 addresses to byte arrays', () => { - assert.deepEqual( + deepEqual( ipaddr.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( + deepEqual( ipaddr.parse('2a00:1450:8007::68').toByteArray(), [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68] ); - assert.deepEqual( + deepEqual( ipaddr.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(ipaddr.IPv6.isValid('1'), false); + equal(ipaddr.IPv4.isValid('1'), true); + deepEqual(new ipaddr.IPv4([0, 0, 0, 1]), ipaddr.parse('1')); }) it('correctly detects IPv4 and IPv6 CIDR addresses', () => { - assert.deepEqual( + deepEqual( [ipaddr.IPv6.parse('fc00::'), 64], ipaddr.parseCIDR('fc00::/64') ); - assert.deepEqual( + deepEqual( [ipaddr.IPv4.parse('1.2.3.4'), 5], ipaddr.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(ipaddr.isValid('4999999999'), false); + equal(ipaddr.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(ipaddr.IPv6.isValid('::8:8:8:8:8:8:8:8:8'), false); + equal(ipaddr.IPv6.isValid('::8:8:8:8:8:8:8:8:8%z'), false); }) it('subnetMatch does not fail on empty range', () => { @@ -505,172 +505,172 @@ describe('ipaddr', () => { }) 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(ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 3, 4]), {}, false), false); + equal(ipaddr.subnetMatch(new ipaddr.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); + equal(ipaddr.subnetMatch(new ipaddr.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); + equal(ipaddr.subnetMatch(new ipaddr.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'); + equal(ipaddr.subnetMatch(new ipaddr.IPv4([1, 2, 4, 1]), rangelist, false), 'dual64'); + equal(ipaddr.subnetMatch(new ipaddr.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(() => { + equal(ipaddr.fromByteArray([0x7f, 0, 0, 1]).kind(), 'ipv4'); + equal(ipaddr.fromByteArray([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]).kind(), 'ipv6'); + throws(() => { ipaddr.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(ipaddr.IPv4.parse('255.255.255.255').prefixLengthFromSubnetMask(), 32); + equal(ipaddr.IPv4.parse('255.255.255.254').prefixLengthFromSubnetMask(), 31); + equal(ipaddr.IPv4.parse('255.255.255.252').prefixLengthFromSubnetMask(), 30); + equal(ipaddr.IPv4.parse('255.255.255.248').prefixLengthFromSubnetMask(), 29); + equal(ipaddr.IPv4.parse('255.255.255.240').prefixLengthFromSubnetMask(), 28); + equal(ipaddr.IPv4.parse('255.255.255.224').prefixLengthFromSubnetMask(), 27); + equal(ipaddr.IPv4.parse('255.255.255.192').prefixLengthFromSubnetMask(), 26); + equal(ipaddr.IPv4.parse('255.255.255.128').prefixLengthFromSubnetMask(), 25); + equal(ipaddr.IPv4.parse('255.255.255.0').prefixLengthFromSubnetMask(), 24); + equal(ipaddr.IPv4.parse('255.255.254.0').prefixLengthFromSubnetMask(), 23); + equal(ipaddr.IPv4.parse('255.255.252.0').prefixLengthFromSubnetMask(), 22); + equal(ipaddr.IPv4.parse('255.255.248.0').prefixLengthFromSubnetMask(), 21); + equal(ipaddr.IPv4.parse('255.255.240.0').prefixLengthFromSubnetMask(), 20); + equal(ipaddr.IPv4.parse('255.255.224.0').prefixLengthFromSubnetMask(), 19); + equal(ipaddr.IPv4.parse('255.255.192.0').prefixLengthFromSubnetMask(), 18); + equal(ipaddr.IPv4.parse('255.255.128.0').prefixLengthFromSubnetMask(), 17); + equal(ipaddr.IPv4.parse('255.255.0.0').prefixLengthFromSubnetMask(), 16); + equal(ipaddr.IPv4.parse('255.254.0.0').prefixLengthFromSubnetMask(), 15); + equal(ipaddr.IPv4.parse('255.252.0.0').prefixLengthFromSubnetMask(), 14); + equal(ipaddr.IPv4.parse('255.248.0.0').prefixLengthFromSubnetMask(), 13); + equal(ipaddr.IPv4.parse('255.240.0.0').prefixLengthFromSubnetMask(), 12); + equal(ipaddr.IPv4.parse('255.224.0.0').prefixLengthFromSubnetMask(), 11); + equal(ipaddr.IPv4.parse('255.192.0.0').prefixLengthFromSubnetMask(), 10); + equal(ipaddr.IPv4.parse('255.128.0.0').prefixLengthFromSubnetMask(), 9); + equal(ipaddr.IPv4.parse('255.0.0.0').prefixLengthFromSubnetMask(), 8); + equal(ipaddr.IPv4.parse('254.0.0.0').prefixLengthFromSubnetMask(), 7); + equal(ipaddr.IPv4.parse('252.0.0.0').prefixLengthFromSubnetMask(), 6); + equal(ipaddr.IPv4.parse('248.0.0.0').prefixLengthFromSubnetMask(), 5); + equal(ipaddr.IPv4.parse('240.0.0.0').prefixLengthFromSubnetMask(), 4); + equal(ipaddr.IPv4.parse('224.0.0.0').prefixLengthFromSubnetMask(), 3); + equal(ipaddr.IPv4.parse('192.0.0.0').prefixLengthFromSubnetMask(), 2); + equal(ipaddr.IPv4.parse('128.0.0.0').prefixLengthFromSubnetMask(), 1); + equal(ipaddr.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(ipaddr.IPv4.parse('192.168.255.0').prefixLengthFromSubnetMask(), null); + equal(ipaddr.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(ipaddr.IPv6.parse('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff').prefixLengthFromSubnetMask(), 128); + equal(ipaddr.IPv6.parse('ffff:ffff:ffff:ffff::').prefixLengthFromSubnetMask(), 64); + equal(ipaddr.IPv6.parse('ffff:ffff:ffff:ff80::').prefixLengthFromSubnetMask(), 57); + equal(ipaddr.IPv6.parse('ffff:ffff:ffff::').prefixLengthFromSubnetMask(), 48); + equal(ipaddr.IPv6.parse('ffff:ffff:ffff::%z').prefixLengthFromSubnetMask(), 48); + equal(ipaddr.IPv6.parse('::').prefixLengthFromSubnetMask(), 0); + equal(ipaddr.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(ipaddr.IPv6.parse('2001:db8::').prefixLengthFromSubnetMask(), null); + equal(ipaddr.IPv6.parse('ffff:0:0:ffff::').prefixLengthFromSubnetMask(), null); + equal(ipaddr.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(ipaddr.IPv4.subnetMaskFromPrefixLength(0), '0.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(1), '128.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(2), '192.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(3), '224.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(4), '240.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(5), '248.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(6), '252.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(7), '254.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(8), '255.0.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(9), '255.128.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(10), '255.192.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(11), '255.224.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(12), '255.240.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(13), '255.248.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(14), '255.252.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(15), '255.254.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(16), '255.255.0.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(17), '255.255.128.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(18), '255.255.192.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(19), '255.255.224.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(20), '255.255.240.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(21), '255.255.248.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(22), '255.255.252.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(23), '255.255.254.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(24), '255.255.255.0'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(25), '255.255.255.128'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(26), '255.255.255.192'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(27), '255.255.255.224'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(28), '255.255.255.240'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(29), '255.255.255.248'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(30), '255.255.255.252'); + equal(ipaddr.IPv4.subnetMaskFromPrefixLength(31), '255.255.255.254'); + equal(ipaddr.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(ipaddr.IPv6.subnetMaskFromPrefixLength(128), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(112), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:0'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(96), 'ffff:ffff:ffff:ffff:ffff:ffff::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(72), 'ffff:ffff:ffff:ffff:ff00::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(64), 'ffff:ffff:ffff:ffff::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(48), 'ffff:ffff:ffff::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(32), 'ffff:ffff::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(16), 'ffff::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(8), 'ff00::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(4), 'f000::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(2), 'c000::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(1), '8000::'); + equal(ipaddr.IPv6.subnetMaskFromPrefixLength(0), '::'); }) 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(ipaddr.IPv4.broadcastAddressFromCIDR('172.0.0.1/24'), '172.0.0.255'); + equal(ipaddr.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(ipaddr.IPv4.networkAddressFromCIDR('172.0.0.1/24'), '172.0.0.0'); + equal(ipaddr.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(ipaddr.IPv6.networkAddressFromCIDR('::/0'), '::'); + equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a::'); + equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f53b::1:1/48'), '2001:db8:f53b::'); + equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f531::1:1/44'), '2001:db8:f530::'); + equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f500::1/40'), '2001:db8:f500::'); + equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db8:f500::1%z/40'), '2001:db8:f500::'); + equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db9:f500::1/40'), '2001:db9:f500::'); + equal(ipaddr.IPv6.networkAddressFromCIDR('2001:db9:f500::1%z/40'), '2001:db9:f500::'); + equal(ipaddr.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(ipaddr.IPv6.broadcastAddressFromCIDR('::/0'), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a:0:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53b::1:1/48'), '2001:db8:f53b:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f531::1:1/44'), '2001:db8:f53f:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f500::1/40'), '2001:db8:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f500::1%z/40'), '2001:db8:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db9:f500::1/40'), '2001:db9:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db9:f500::1%z/40'), '2001:db9:f5ff:ffff:ffff:ffff:ffff:ffff'); + equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1/128'), '2001:db8:f53a::1'); }) })