| 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<T> { | ||
| [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<IPv4 | IPv6>, 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<IPv4>, 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<IPv6>, defaultName?: string): string; | ||
| toIPv4Address(): IPv4; | ||
| toRFC5952String(): string; | ||
| } | ||
| } | ||
| export = Address; | ||
| } |
+38
-22
@@ -64,4 +64,5 @@ (function (root) { | ||
| // The following loop would hang if colonCount > parts | ||
| if (colonCount > parts) { | ||
| // An address must not contain more separators than available parts, | ||
| // and :: must compress at least one part. | ||
| if (colonCount >= parts) { | ||
| return null; | ||
@@ -95,3 +96,3 @@ } | ||
| for (let i = 0; i < ref.length; i++) { | ||
| results.push(parseInt(ref[i], 16)); | ||
| results.push(ref[i].length > 4 ? NaN : parseInt(ref[i], 16)); | ||
| } | ||
@@ -332,3 +333,3 @@ | ||
| } catch (e) { | ||
| throw new Error('ipaddr: the address does not have IPv4 CIDR format'); | ||
| throw new Error('ipaddr: the address does not have IPv4 CIDR format', { cause: e }); | ||
| } | ||
@@ -347,3 +348,3 @@ }; | ||
| return true; | ||
| } catch (e) { | ||
| } catch { | ||
| return false; | ||
@@ -358,3 +359,3 @@ } | ||
| return true; | ||
| } catch (e) { | ||
| } catch { | ||
| return false; | ||
@@ -402,3 +403,3 @@ } | ||
| } catch (e) { | ||
| throw new Error('ipaddr: the address does not have IPv4 CIDR format'); | ||
| throw new Error('ipaddr: the address does not have IPv4 CIDR format', { cause: e }); | ||
| } | ||
@@ -516,3 +517,3 @@ }; | ||
| prefix = parseInt(prefix); | ||
| if (prefix < 0 || prefix > 32) { | ||
| if (Number.isNaN(prefix) || prefix < 0 || prefix > 32) { | ||
| throw new Error('ipaddr: invalid IPv4 prefix length'); | ||
@@ -782,9 +783,24 @@ } | ||
| const regex = /((^|:)(0(:|$)){2,})/g; | ||
| const string = this.toNormalizedString(); | ||
| // The zone identifier (RFC 4007) is not part of the address; match | ||
| // against the address alone so a trailing zero run right before the | ||
| // "%" suffix still gets compressed (RFC 5952, 4.2.2). | ||
| let suffix = ''; | ||
| if (this.zoneId) { | ||
| suffix = `%${this.zoneId}`; | ||
| } | ||
| const normalized = this.toNormalizedString(); | ||
| const string = normalized.slice(0, normalized.length - suffix.length); | ||
| let bestMatchIndex = 0; | ||
| let bestMatchLength = -1; | ||
| let bestMatchGroups = -1; | ||
| let match; | ||
| while ((match = regex.exec(string))) { | ||
| if (match[0].length > bestMatchLength) { | ||
| // Compare by the number of zero groups, not the matched length: | ||
| // a run that is not at the start carries a leading ":" in the | ||
| // match, so an equal-length run later in the address would | ||
| // otherwise be preferred over the leftmost one (RFC 5952, 4.2.3). | ||
| const groups = (match[0].match(/0/g) || []).length; | ||
| if (groups > bestMatchGroups) { | ||
| bestMatchGroups = groups; | ||
| bestMatchIndex = match.index; | ||
@@ -796,6 +812,6 @@ bestMatchLength = match[0].length; | ||
| if (bestMatchLength < 0) { | ||
| return string; | ||
| return string + suffix; | ||
| } | ||
| return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}`; | ||
| return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}${suffix}`; | ||
| }; | ||
@@ -830,3 +846,3 @@ | ||
| } catch (e) { | ||
| throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); | ||
| throw new Error('ipaddr: the address does not have IPv6 CIDR format', { cause: e }); | ||
| } | ||
@@ -853,3 +869,3 @@ }; | ||
| return true; | ||
| } catch (e) { | ||
| } catch { | ||
| return false; | ||
@@ -870,3 +886,3 @@ } | ||
| return true; | ||
| } catch (e) { | ||
| } catch { | ||
| return false; | ||
@@ -894,3 +910,3 @@ } | ||
| } catch (e) { | ||
| throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); | ||
| throw new Error('ipaddr: the address does not have IPv6 CIDR format', { cause: e }); | ||
| } | ||
@@ -904,3 +920,3 @@ }; | ||
| if (addr.parts === null) { | ||
| if (addr === null) { | ||
| throw new Error('ipaddr: string is not formatted like an IPv6 Address'); | ||
@@ -948,3 +964,3 @@ } | ||
| addr = expandIPv6(addr + zoneId, 6); | ||
| if (addr.parts) { | ||
| if (addr && addr.parts) { | ||
| octets = [ | ||
@@ -978,3 +994,3 @@ parseInt(match[2]), | ||
| prefix = parseInt(prefix); | ||
| if (prefix < 0 || prefix > 128) { | ||
| if (Number.isNaN(prefix) || prefix < 0 || prefix > 128) { | ||
| throw new Error('ipaddr: invalid IPv6 prefix length'); | ||
@@ -1040,7 +1056,7 @@ } | ||
| return ipaddr.IPv6.parseCIDR(string); | ||
| } catch (e) { | ||
| } catch { | ||
| try { | ||
| return ipaddr.IPv4.parseCIDR(string); | ||
| } catch (e2) { | ||
| throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format'); | ||
| } catch (e) { | ||
| throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format', { cause: e }); | ||
| } | ||
@@ -1047,0 +1063,0 @@ } |
+13
-10
| { | ||
| "name": "ipaddr.js", | ||
| "description": "A library for manipulating IPv4 and IPv6 addresses in JavaScript.", | ||
| "version": "2.4.0", | ||
| "type": "commonjs", | ||
| "version": "2.5.0", | ||
| "author": "whitequark <whitequark@whitequark.org>", | ||
@@ -9,6 +10,6 @@ "directories": { | ||
| }, | ||
| "dependencies": {}, | ||
| "devDependencies": { | ||
| "eslint": "^9.19.0", | ||
| "uglify-es": "*" | ||
| "@eslint/js": "^10.0.1", | ||
| "eslint": "^10.3.0", | ||
| "globals": "^17.6.0" | ||
| }, | ||
@@ -18,8 +19,7 @@ "scripts": { | ||
| "lintfix": "npx eslint --fix lib test", | ||
| "build": "npx uglifyjs --compress --mangle --wrap=window -o ipaddr.min.js lib/ipaddr.js", | ||
| "test": "node --test" | ||
| "test": "node --test", | ||
| "test:coverage": "node --test --experimental-test-coverage" | ||
| }, | ||
| "files": [ | ||
| "lib", | ||
| "ipaddr.min.js" | ||
| "lib" | ||
| ], | ||
@@ -31,3 +31,6 @@ "keywords": [ | ||
| ], | ||
| "repository": "git://github.com/whitequark/ipaddr.js", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/whitequark/ipaddr.js.git" | ||
| }, | ||
| "main": "./lib/ipaddr.js", | ||
@@ -38,3 +41,3 @@ "engines": { | ||
| "license": "MIT", | ||
| "types": "./lib/ipaddr.js.d.ts" | ||
| "types": "./lib/ipaddr.d.ts" | ||
| } |
+527
-124
@@ -1,229 +0,632 @@ | ||
| # ipaddr.js — an IPv6 and IPv4 address manipulation library | ||
| # ipaddr.js — an IPv4 and IPv6 address manipulation library | ||
| [](https://github.com/whitequark/ipaddr.js/actions?query=workflow%3A%22CI+Tests%22) | ||
| [![CI Tests][ci-img]][ci-url] | ||
| ipaddr.js is a small (1.9K minified and gzipped) library for manipulating | ||
| IP addresses in JavaScript environments. It runs on both CommonJS runtimes | ||
| (e.g. [nodejs]) and in a web browser. | ||
| ipaddr.js is a library for manipulating IPv4 and IPv6 addresses in JavaScript. It runs in Node.js and in browsers. | ||
| ipaddr.js allows you to verify and parse string representation of an IP | ||
| address, match it against a CIDR range or range list, determine if it falls | ||
| into some reserved ranges (examples include loopback and private ranges), | ||
| and convert between IPv4 and IPv4-mapped IPv6 addresses. | ||
| It lets you parse and validate addresses, match them against CIDR ranges, classify them into named ranges (loopback, private, reserved, etc.), convert between IPv4 and IPv6 representations, and work with raw byte arrays. | ||
| [nodejs]: http://nodejs.org | ||
| ## Installation | ||
| `npm install ipaddr.js` | ||
| ``` | ||
| npm install ipaddr.js | ||
| ``` | ||
| ## Older Node support | ||
| ## Node.js version support | ||
| Use 2.x release for nodejs versions 10+. | ||
| Use the 1.x release for versions of nodejs older than 10. | ||
| - ipaddr.js 2.x — Node.js 10+ | ||
| - ipaddr.js 1.x — Node.js < 10 | ||
| ## API | ||
| ## Quick start | ||
| ipaddr.js defines one object in the global scope: `ipaddr`. In CommonJS, | ||
| it is exported from the module: | ||
| ```js | ||
| const ipaddr = require('ipaddr.js'); | ||
| ipaddr.isValid('192.168.1.1'); // => true | ||
| ipaddr.isValid('2001:db8::1'); // => true | ||
| ipaddr.isValid('not an address'); // => false | ||
| const addr = ipaddr.parse('2001:db8::1'); | ||
| addr.kind(); // => 'ipv6' | ||
| addr.toString(); // => '2001:db8::1' | ||
| const [network, prefix] = ipaddr.parseCIDR('10.0.0.0/8'); | ||
| network.toString(); // => '10.0.0.0' | ||
| prefix; // => 8 | ||
| ``` | ||
| The API consists of several global methods and two classes: ipaddr.IPv6 and ipaddr.IPv4. | ||
| ## Table of contents | ||
| ### Global methods | ||
| - [Global API](#global-api) | ||
| - [isValid](#ipaddrIsValid) | ||
| - [isValidCIDR](#ipaddrIsValidCIDR) | ||
| - [parse](#ipaddrParse) | ||
| - [parseCIDR](#ipaddrParseCIDR) | ||
| - [process](#ipaddrProcess) | ||
| - [subnetMatch](#ipaddrSubnetMatch) | ||
| - [fromByteArray](#ipaddrFromByteArray) | ||
| - [IPv4 API](#ipv4-api) | ||
| - Static: [isValid](#ipv4-isvalid), [isValidCIDR](#ipv4-isvalidcidr), [isValidFourPartDecimal](#ipv4-isvalidfourpartdecimal), [isValidCIDRFourPartDecimal](#ipv4-isvalidcidrfourpartdecimal), [isIPv4](#ipv4-isipv4), [parse](#ipv4-parse), [parseCIDR](#ipv4-parsecidr), [broadcastAddressFromCIDR](#ipv4-broadcastaddressfromcidr), [networkAddressFromCIDR](#ipv4-networkaddressfromcidr), [subnetMaskFromPrefixLength](#ipv4-subnetmaskfromprefixlength) | ||
| - Instance: [octets](#ipv4-octets), [kind()](#ipv4-kind), [match()](#ipv4-match), [range()](#ipv4-range), [prefixLengthFromSubnetMask()](#ipv4-prefixlengthfromsubnetmask), [subnetMatch()](#ipv4-subnetmatch-instance), [toByteArray()](#ipv4-tobytearray), [toIPv4MappedAddress()](#ipv4-toipv4mappedaddress), [toNormalizedString()](#ipv4-tonormalizedstring), [toString()](#ipv4-tostring) | ||
| - [IPv6 API](#ipv6-api) | ||
| - Static: [isValid](#ipv6-isvalid), [isValidCIDR](#ipv6-isvalidcidr), [isIPv6](#ipv6-isipv6), [parse](#ipv6-parse), [parseCIDR](#ipv6-parsecidr), [broadcastAddressFromCIDR](#ipv6-broadcastaddressfromcidr), [networkAddressFromCIDR](#ipv6-networkaddressfromcidr), [subnetMaskFromPrefixLength](#ipv6-subnetmaskfromprefixlength) | ||
| - Instance: [parts](#ipv6-parts), [zoneId](#ipv6-zoneid), [kind()](#ipv6-kind), [isIPv4MappedAddress()](#ipv6-isipv4mappedaddress), [match()](#ipv6-match), [range()](#ipv6-range), [prefixLengthFromSubnetMask()](#ipv6-prefixlengthfromsubnetmask), [subnetMatch()](#ipv6-subnetmatch-instance), [toByteArray()](#ipv6-tobytearray), [toFixedLengthString()](#ipv6-tofixedlengthstring), [toIPv4Address()](#ipv6-toipv4address), [toNormalizedString()](#ipv6-tonormalizedstring), [toRFC5952String()](#ipv6-torfc5952string), [toString()](#ipv6-tostring) | ||
| There are four global methods defined: `ipaddr.isValid`, `ipaddr.isValidCIDR`, | ||
| `ipaddr.parse`, and `ipaddr.process`. All of them receive a string as a single | ||
| parameter. | ||
| --- | ||
| The `ipaddr.isValid` method returns `true` if the address is a valid IPv4 or | ||
| IPv6 address, and `false` otherwise. It does not throw any exceptions. | ||
| ## Global API | ||
| The `ipaddr.isValidCIDR` method returns `true` if the address is a valid IPv4 or | ||
| IPv6 address in CIDR notation, and `false` otherwise. It does not throw any exceptions. | ||
| ```js | ||
| const ipaddr = require('ipaddr.js'); | ||
| ``` | ||
| The `ipaddr.parse` method returns an object representing the IP address, | ||
| or throws an `Error` if the passed string is not a valid representation of an | ||
| IP address. | ||
| <a name="ipaddrIsValid"></a> | ||
| ### `ipaddr.isValid(string)` | ||
| The `ipaddr.process` method works just like the `ipaddr.parse` one, but it | ||
| automatically converts IPv4-mapped IPv6 addresses to their IPv4 counterparts | ||
| before returning. It is useful when you have a Node.js instance listening | ||
| on an IPv6 socket, and the `net.ipv6.bindv6only` sysctl parameter (or its | ||
| equivalent on non-Linux OS) is set to 0. In this case, you can accept IPv4 | ||
| connections on your IPv6-only socket, but the remote address will be mangled. | ||
| Use `ipaddr.process` method to automatically demangle it. | ||
| Returns `true` if the string is a valid IPv4 or IPv6 address; `false` otherwise. Never throws. | ||
| ### Object representation | ||
| ```js | ||
| ipaddr.isValid('192.168.1.1'); // => true | ||
| ipaddr.isValid('2001:db8::1'); // => true | ||
| ipaddr.isValid('999.0.0.1'); // => false | ||
| ipaddr.isValid('hello'); // => false | ||
| ``` | ||
| Parsing methods return an object which descends from `ipaddr.IPv6` or | ||
| `ipaddr.IPv4`. These objects share some properties, but most of them differ. | ||
| <a name="ipaddrIsValidCIDR"></a> | ||
| ### `ipaddr.isValidCIDR(string)` | ||
| #### Shared properties | ||
| Returns `true` if the string is a valid IPv4 or IPv6 address in CIDR notation; `false` otherwise. Never throws. | ||
| One can determine the type of address by calling `addr.kind()`. It will return | ||
| either `"ipv6"` or `"ipv4"`. | ||
| ```js | ||
| ipaddr.isValidCIDR('192.168.0.0/24'); // => true | ||
| ipaddr.isValidCIDR('2001:db8::/32'); // => true | ||
| ipaddr.isValidCIDR('192.168.0.1/33'); // => false | ||
| ``` | ||
| An address can be converted back to its string representation with `addr.toString()`. | ||
| Note that this method: | ||
| * does not return the original string used to create the object (in fact, there is | ||
| no way of getting that string) | ||
| * returns a compact representation (when it is applicable) | ||
| <a name="ipaddrParse"></a> | ||
| ### `ipaddr.parse(string)` | ||
| A `match(range, bits)` method can be used to check if the address falls into a | ||
| certain CIDR range. Note that an address can be (obviously) matched only against an address of the same type. | ||
| Parses the string and returns an `IPv4` or `IPv6` object. Throws if the string is not a valid address. | ||
| For example: | ||
| ```js | ||
| const addr = ipaddr.parse('2001:db8:1234::1'); | ||
| const range = ipaddr.parse('2001:db8::'); | ||
| const v4 = ipaddr.parse('192.168.1.1'); | ||
| v4.kind(); // => 'ipv4' | ||
| v4.toString(); // => '192.168.1.1' | ||
| addr.match(range, 32); // => true | ||
| const v6 = ipaddr.parse('2001:db8::1'); | ||
| v6.kind(); // => 'ipv6' | ||
| v6.toString(); // => '2001:db8::1' | ||
| ``` | ||
| Alternatively, `match` can also be called as `match([range, bits])`. In this way, it can be used together with the `parseCIDR(string)` method, which parses an IP address together with a CIDR range. | ||
| <a name="ipaddrParseCIDR"></a> | ||
| ### `ipaddr.parseCIDR(string)` | ||
| For example: | ||
| Parses an IP address with a CIDR prefix length and returns a two-element array `[address, prefixLength]`. Throws if the input is not valid CIDR notation. | ||
| ```js | ||
| const addr = ipaddr.parse('2001:db8:1234::1'); | ||
| const [addr, prefix] = ipaddr.parseCIDR('192.168.1.0/24'); | ||
| addr.toString(); // => '192.168.1.0' | ||
| prefix; // => 24 | ||
| ``` | ||
| addr.match(ipaddr.parseCIDR('2001:db8::/32')); // => true | ||
| The returned array can be passed directly to `addr.match()`: | ||
| ```js | ||
| const addr = ipaddr.parse('192.168.1.42'); | ||
| addr.match(ipaddr.parseCIDR('192.168.1.0/24')); // => true | ||
| ``` | ||
| A `range()` method returns one of predefined names for several special ranges defined by IP protocols. The exact names (and their respective CIDR ranges) can be looked up in the source: [IPv6 ranges] and [IPv4 ranges]. Some common ones include `"unicast"` (the default one) and `"reserved"`. | ||
| <a name="ipaddrProcess"></a> | ||
| ### `ipaddr.process(string)` | ||
| You can match against your own range list by using | ||
| `ipaddr.subnetMatch(address, rangeList, defaultName)` method. It can work with a mix of IPv6 or IPv4 addresses, and accepts a name-to-subnet map as the range list. For example: | ||
| Like `ipaddr.parse()`, but automatically converts IPv4-mapped IPv6 addresses (e.g. `::ffff:192.168.1.1`) to their IPv4 equivalents. All other addresses are returned as-is. | ||
| This is useful when accepting connections on a dual-stack IPv6 socket, where IPv4 client addresses appear as IPv4-mapped IPv6 addresses. | ||
| ```js | ||
| ipaddr.process('::ffff:192.168.1.1').toString(); // => '192.168.1.1' | ||
| ipaddr.process('::ffff:192.168.1.1').kind(); // => 'ipv4' | ||
| ipaddr.process('2001:db8::1').kind(); // => 'ipv6' | ||
| ipaddr.process('192.168.1.1').kind(); // => 'ipv4' | ||
| ``` | ||
| <a name="ipaddrSubnetMatch"></a> | ||
| ### `ipaddr.subnetMatch(address, rangeList[, defaultName])` | ||
| Matches `address` against a map of named CIDR ranges and returns the name of the first matching range. Returns `defaultName` (default: `'unicast'`) if no range matches. | ||
| Each `rangeList` value is either a single `[address, prefixLength]` pair or an array of such pairs. The list may mix IPv4 and IPv6 entries; entries of the wrong address family are safely skipped. | ||
| ```js | ||
| const rangeList = { | ||
| documentationOnly: [ ipaddr.parse('2001:db8::'), 32 ], | ||
| tunnelProviders: [ | ||
| [ ipaddr.parse('2001:470::'), 32 ], // he.net | ||
| [ ipaddr.parse('2001:5c0::'), 32 ] // freenet6 | ||
| ] | ||
| private: [ | ||
| [ipaddr.parse('10.0.0.0'), 8], | ||
| [ipaddr.parse('172.16.0.0'), 12], | ||
| [ipaddr.parse('192.168.0.0'), 16], | ||
| ], | ||
| loopback: [ ipaddr.parse('127.0.0.0'), 8 ], | ||
| }; | ||
| ipaddr.subnetMatch(ipaddr.parse('2001:470:8:66::1'), rangeList, 'unknown'); // => "tunnelProviders" | ||
| ipaddr.subnetMatch(ipaddr.parse('192.168.1.1'), rangeList); // => 'private' | ||
| ipaddr.subnetMatch(ipaddr.parse('127.0.0.1'), rangeList); // => 'loopback' | ||
| ipaddr.subnetMatch(ipaddr.parse('8.8.8.8'), rangeList, 'public'); // => 'public' | ||
| ipaddr.subnetMatch(ipaddr.parse('8.8.8.8'), rangeList); // => 'unicast' | ||
| ``` | ||
| The addresses can be converted to their byte representation with `toByteArray()`. (Actually, JavaScript mostly does not know about byte buffers. They are emulated with arrays of numbers, each in range of 0..255.) | ||
| <a name="ipaddrFromByteArray"></a> | ||
| ### `ipaddr.fromByteArray(bytes)` | ||
| Constructs an IPv4 or IPv6 address from a byte array in network byte order (MSB first). Accepts 4 bytes for IPv4, or 16 bytes for IPv6. Throws if the array length is not 4 or 16. | ||
| ```js | ||
| const bytes = ipaddr.parse('2a00:1450:8007::68').toByteArray(); // ipv6.google.com | ||
| bytes // => [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, <zeroes...>, 0x00, 0x68 ] | ||
| ipaddr.fromByteArray([127, 0, 0, 1]).toString(); // => '127.0.0.1' | ||
| ipaddr.fromByteArray([ | ||
| 0x20, 0x01, 0x0d, 0xb8, | ||
| 0x00, 0x00, 0x00, 0x00, | ||
| 0x00, 0x00, 0x00, 0x00, | ||
| 0x00, 0x00, 0x00, 0x01, | ||
| ]).toString(); // => '2001:db8::1' | ||
| ``` | ||
| The `ipaddr.IPv4` and `ipaddr.IPv6` objects have some methods defined, too. All of them have the same interface for both protocols, and are similar to global methods. | ||
| --- | ||
| `ipaddr.IPvX.isValid(string)` can be used to check if the string is a valid address for particular protocol, and `ipaddr.IPvX.parse(string)` is the error-throwing parser. | ||
| ## IPv4 API | ||
| `ipaddr.IPvX.isValid(string)` uses the same format for parsing as the POSIX `inet_ntoa` function, which accepts unusual formats like `0xc0.168.1.1` or `0x10000000`. The function `ipaddr.IPv4.isValidFourPartDecimal(string)` validates the IPv4 address and also ensures that it is written in four-part decimal format. | ||
| `ipaddr.IPv4.isValidCIDRFourPartDecimal(string)` validates an IPv4 address in CIDR notation and also ensures that its address portion is written in four-part decimal format. | ||
| ### Static methods | ||
| [IPv6 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L530 | ||
| [IPv4 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L182 | ||
| <a name="ipv4-isvalid"></a> | ||
| #### `ipaddr.IPv4.isValid(string)` | ||
| #### IPv6 properties | ||
| Returns `true` if the string is a valid IPv4 address; `false` otherwise. Never throws. | ||
| Sometimes you will want to convert IPv6 not to a compact string representation (with the `::` substitution); the `toNormalizedString()` method will return an address where all zeroes are explicit. | ||
| Accepts the same extended formats as `ipaddr.IPv4.parse()` (hex, octal, fewer-than-four parts). For strict four-part decimal validation, use `isValidFourPartDecimal()`. | ||
| For example: | ||
| ```js | ||
| ipaddr.IPv4.isValid('192.168.1.1'); // => true | ||
| ipaddr.IPv4.isValid('0xc0.168.1.1'); // => true (hex octet) | ||
| ipaddr.IPv4.isValid('999.0.0.1'); // => false | ||
| ``` | ||
| <a name="ipv4-isvalidcidr"></a> | ||
| #### `ipaddr.IPv4.isValidCIDR(string)` | ||
| Returns `true` if the string is a valid IPv4 CIDR address; `false` otherwise. Never throws. | ||
| ```js | ||
| const addr = ipaddr.parse('2001:0db8::0001'); | ||
| addr.toString(); // => '2001:db8::1' | ||
| addr.toNormalizedString(); // => '2001:db8:0:0:0:0:0:1' | ||
| ipaddr.IPv4.isValidCIDR('192.168.0.0/24'); // => true | ||
| ipaddr.IPv4.isValidCIDR('192.168.0.0/33'); // => false | ||
| ``` | ||
| The `isIPv4MappedAddress()` method will return `true` if this address is an IPv4-mapped | ||
| one, and `toIPv4Address()` will return an IPv4 object address. | ||
| <a name="ipv4-isvalidfourpartdecimal"></a> | ||
| #### `ipaddr.IPv4.isValidFourPartDecimal(string)` | ||
| To access the underlying binary representation of the address, use `addr.parts`. | ||
| Like `isValid()`, but only accepts the standard four-part dotted-decimal format. Rejects hex, octal, and other non-standard notations accepted by `inet_aton`. | ||
| ```js | ||
| const addr = ipaddr.parse('2001:db8:10::1234:DEAD'); | ||
| addr.parts // => [0x2001, 0xdb8, 0x10, 0, 0, 0, 0x1234, 0xdead] | ||
| ipaddr.IPv4.isValidFourPartDecimal('192.168.1.1'); // => true | ||
| ipaddr.IPv4.isValidFourPartDecimal('0xc0.168.1.1'); // => false | ||
| ``` | ||
| A IPv6 zone index can be accessed via `addr.zoneId`: | ||
| <a name="ipv4-isvalidcidrfourpartdecimal"></a> | ||
| #### `ipaddr.IPv4.isValidCIDRFourPartDecimal(string)` | ||
| Like `isValidCIDR()`, but only accepts the standard four-part dotted-decimal format for the address portion. | ||
| ```js | ||
| const addr = ipaddr.parse('2001:db8::%eth0'); | ||
| addr.zoneId // => 'eth0' | ||
| ipaddr.IPv4.isValidCIDRFourPartDecimal('192.168.0.0/24'); // => true | ||
| ipaddr.IPv4.isValidCIDRFourPartDecimal('0xc0.168.0.0/24'); // => false | ||
| ``` | ||
| #### IPv4 properties | ||
| <a name="ipv4-isipv4"></a> | ||
| #### `ipaddr.IPv4.isIPv4(string)` | ||
| `toIPv4MappedAddress()` will return a corresponding IPv4-mapped IPv6 address. | ||
| Returns `true` if the string matches the IPv4 address pattern. This is a lightweight regex check — it does not validate that all octets are in range (0–255). Prefer `isValid()` for authoritative validation. | ||
| To access the underlying representation of the address, use `addr.octets`. | ||
| ```js | ||
| ipaddr.IPv4.isIPv4('192.168.1.1'); // => true | ||
| ipaddr.IPv4.isIPv4('2001:db8::1'); // => false | ||
| ``` | ||
| <a name="ipv4-parse"></a> | ||
| #### `ipaddr.IPv4.parse(string)` | ||
| Parses the string and returns an `IPv4` object. Throws if the string is not a valid IPv4 address. | ||
| In addition to standard dotted-decimal, the parser accepts formats recognised by the POSIX `inet_aton` function: hex octets (`0xc0.0xa8.0x01.0x01`), octal octets (`0300.0250.01.01`), three-part (`192.168.257`), two-part (`192.11010049`), and single-value (`3232235777`) notation. | ||
| ```js | ||
| ipaddr.IPv4.parse('192.168.1.1').toString(); // => '192.168.1.1' | ||
| ipaddr.IPv4.parse('0xc0.168.1.1').toString(); // => '192.168.1.1' | ||
| ``` | ||
| <a name="ipv4-parsecidr"></a> | ||
| #### `ipaddr.IPv4.parseCIDR(string)` | ||
| Parses an IPv4 CIDR address and returns `[IPv4, prefixLength]`. Throws if the input is invalid. | ||
| ```js | ||
| const [addr, prefix] = ipaddr.IPv4.parseCIDR('192.168.1.0/24'); | ||
| addr.toString(); // => '192.168.1.0' | ||
| prefix; // => 24 | ||
| ``` | ||
| <a name="ipv4-broadcastaddressfromcidr"></a> | ||
| #### `ipaddr.IPv4.broadcastAddressFromCIDR(string)` | ||
| Returns the broadcast address for the given IPv4 CIDR block. | ||
| ```js | ||
| ipaddr.IPv4.broadcastAddressFromCIDR('192.168.1.0/24').toString(); // => '192.168.1.255' | ||
| ipaddr.IPv4.broadcastAddressFromCIDR('10.0.0.1/8').toString(); // => '10.255.255.255' | ||
| ``` | ||
| <a name="ipv4-networkaddressfromcidr"></a> | ||
| #### `ipaddr.IPv4.networkAddressFromCIDR(string)` | ||
| Returns the network address for the given IPv4 CIDR block. | ||
| ```js | ||
| ipaddr.IPv4.networkAddressFromCIDR('192.168.1.42/24').toString(); // => '192.168.1.0' | ||
| ipaddr.IPv4.networkAddressFromCIDR('10.1.2.3/8').toString(); // => '10.0.0.0' | ||
| ``` | ||
| <a name="ipv4-subnetmaskfromprefixlength"></a> | ||
| #### `ipaddr.IPv4.subnetMaskFromPrefixLength(prefix)` | ||
| Returns the IPv4 subnet mask corresponding to the given CIDR prefix length. | ||
| ```js | ||
| ipaddr.IPv4.subnetMaskFromPrefixLength(24).toString(); // => '255.255.255.0' | ||
| ipaddr.IPv4.subnetMaskFromPrefixLength(16).toString(); // => '255.255.0.0' | ||
| ``` | ||
| ### Instance properties and methods | ||
| <a name="ipv4-octets"></a> | ||
| #### `addr.octets` | ||
| The four octets of the address as an array of numbers. | ||
| ```js | ||
| ipaddr.parse('192.168.1.1').octets; // => [192, 168, 1, 1] | ||
| ``` | ||
| <a name="ipv4-kind"></a> | ||
| #### `addr.kind()` | ||
| Always returns `'ipv4'`. | ||
| ```js | ||
| ipaddr.parse('192.168.1.1').kind(); // => 'ipv4' | ||
| ``` | ||
| <a name="ipv4-match"></a> | ||
| #### `addr.match(other, cidrBits)` / `addr.match([address, cidrBits])` | ||
| Returns `true` if the address falls within the given CIDR range. | ||
| ```js | ||
| const addr = ipaddr.parse('192.168.1.42'); | ||
| addr.match(ipaddr.parse('192.168.1.0'), 24); // => true | ||
| addr.match(ipaddr.parse('10.0.0.0'), 8); // => false | ||
| // Accepts the tuple returned by parseCIDR: | ||
| addr.match(ipaddr.parseCIDR('192.168.1.0/24')); // => true | ||
| ``` | ||
| <a name="ipv4-range"></a> | ||
| #### `addr.range()` | ||
| Returns the name of the built-in range the address belongs to, or `'unicast'` if it does not match any special range. Recognised names: `'unspecified'`, `'broadcast'`, `'multicast'`, `'linkLocal'`, `'loopback'`, `'carrierGradeNat'`, `'private'`, `'reserved'`, `'as112'`, `'amt'`. | ||
| See the [source][IPv4 ranges] for the full CIDR list associated with each name. | ||
| ```js | ||
| ipaddr.parse('127.0.0.1').range(); // => 'loopback' | ||
| ipaddr.parse('192.168.1.1').range(); // => 'private' | ||
| ipaddr.parse('169.254.1.1').range(); // => 'linkLocal' | ||
| ipaddr.parse('8.8.8.8').range(); // => 'unicast' | ||
| ``` | ||
| <a name="ipv4-prefixlengthfromsubnetmask"></a> | ||
| #### `addr.prefixLengthFromSubnetMask()` | ||
| Returns the CIDR prefix length if this address is a valid contiguous subnet mask, or `null` otherwise. | ||
| ```js | ||
| ipaddr.parse('255.255.255.0').prefixLengthFromSubnetMask(); // => 24 | ||
| ipaddr.parse('255.255.255.240').prefixLengthFromSubnetMask(); // => 28 | ||
| ipaddr.parse('255.192.168.0').prefixLengthFromSubnetMask(); // => null | ||
| ``` | ||
| <a name="ipv4-subnetmatch-instance"></a> | ||
| #### `addr.subnetMatch(rangeList[, defaultName])` | ||
| Instance-method shorthand for `ipaddr.subnetMatch(addr, rangeList, defaultName)`. | ||
| ```js | ||
| const addr = ipaddr.parse('192.168.1.1'); | ||
| addr.octets // => [192, 168, 1, 1] | ||
| addr.subnetMatch({ private: [ipaddr.parse('192.168.0.0'), 16] }); // => 'private' | ||
| ``` | ||
| `prefixLengthFromSubnetMask()` will return a CIDR prefix length for a valid IPv4 netmask or | ||
| null if the netmask is not valid. | ||
| <a name="ipv4-tobytearray"></a> | ||
| #### `addr.toByteArray()` | ||
| Returns the address as an array of four bytes in network byte order. | ||
| ```js | ||
| ipaddr.IPv4.parse('255.255.255.240').prefixLengthFromSubnetMask() == 28 | ||
| ipaddr.IPv4.parse('255.192.164.0').prefixLengthFromSubnetMask() == null | ||
| ipaddr.parse('192.168.1.1').toByteArray(); // => [192, 168, 1, 1] | ||
| ipaddr.parse('127.0.0.1').toByteArray(); // => [127, 0, 0, 1] | ||
| ``` | ||
| `subnetMaskFromPrefixLength()` will return an IPv4 netmask for a valid CIDR prefix length. | ||
| <a name="ipv4-toipv4mappedaddress"></a> | ||
| #### `addr.toIPv4MappedAddress()` | ||
| Returns the IPv4-mapped IPv6 representation of this address (`::ffff:x.x.x.x`). | ||
| ```js | ||
| ipaddr.IPv4.subnetMaskFromPrefixLength(24) == '255.255.255.0' | ||
| ipaddr.IPv4.subnetMaskFromPrefixLength(29) == '255.255.255.248' | ||
| ipaddr.parse('192.168.1.1').toIPv4MappedAddress().toString(); // => '::ffff:c0a8:101' | ||
| ``` | ||
| `broadcastAddressFromCIDR()` will return the broadcast address for a given IPv4 interface and netmask in CIDR notation. | ||
| <a name="ipv4-tonormalizedstring"></a> | ||
| #### `addr.toNormalizedString()` | ||
| Returns the address in standard four-part dotted-decimal notation. For IPv4, this is the same as `toString()`. | ||
| ```js | ||
| ipaddr.IPv4.broadcastAddressFromCIDR('172.0.0.1/24') == '172.0.0.255' | ||
| ipaddr.parse('192.168.1.1').toNormalizedString(); // => '192.168.1.1' | ||
| ``` | ||
| `networkAddressFromCIDR()` will return the network address for a given IPv4 interface and netmask in CIDR notation. | ||
| <a name="ipv4-tostring"></a> | ||
| #### `addr.toString()` | ||
| Returns the address as a dotted-decimal string. | ||
| ```js | ||
| ipaddr.IPv4.networkAddressFromCIDR('172.0.0.1/24') == '172.0.0.0' | ||
| ipaddr.parse('192.168.001.001').toString(); // => '192.168.1.1' | ||
| ``` | ||
| #### Conversion | ||
| --- | ||
| IPv4 and IPv6 can be converted bidirectionally to and from network byte order (MSB) byte arrays. | ||
| ## IPv6 API | ||
| The `fromByteArray()` method will take an array and create an appropriate IPv4 or IPv6 object | ||
| if the input satisfies the requirements. For IPv4 it has to be an array of four 8-bit values, | ||
| while for IPv6 it has to be an array of sixteen 8-bit values. | ||
| ### Static methods | ||
| For example: | ||
| <a name="ipv6-isvalid"></a> | ||
| #### `ipaddr.IPv6.isValid(string)` | ||
| Returns `true` if the string is a valid IPv6 address; `false` otherwise. Never throws. Accepts addresses with a zone ID (e.g. `fe80::1%eth0`). | ||
| ```js | ||
| const addr = ipaddr.fromByteArray([0x7f, 0, 0, 1]); | ||
| addr.toString(); // => '127.0.0.1' | ||
| ipaddr.IPv6.isValid('2001:db8::1'); // => true | ||
| ipaddr.IPv6.isValid('::1'); // => true | ||
| ipaddr.IPv6.isValid('fe80::1%eth0'); // => true | ||
| ipaddr.IPv6.isValid('192.168.1.1'); // => false | ||
| ``` | ||
| or | ||
| <a name="ipv6-isvalidcidr"></a> | ||
| #### `ipaddr.IPv6.isValidCIDR(string)` | ||
| Returns `true` if the string is a valid IPv6 CIDR address; `false` otherwise. Never throws. | ||
| ```js | ||
| const addr = ipaddr.fromByteArray([0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) | ||
| addr.toString(); // => '2001:db8::1' | ||
| ipaddr.IPv6.isValidCIDR('2001:db8::/32'); // => true | ||
| ipaddr.IPv6.isValidCIDR('2001:db8::/129'); // => false | ||
| ``` | ||
| Both objects also offer a `toByteArray()` method, which returns an array in network byte order (MSB). | ||
| <a name="ipv6-isipv6"></a> | ||
| #### `ipaddr.IPv6.isIPv6(string)` | ||
| For example: | ||
| Returns `true` if the string matches the IPv6 address pattern. This is a lightweight regex check and does not fully validate all groups. Prefer `isValid()` for authoritative validation. | ||
| ```js | ||
| const addr = ipaddr.parse('127.0.0.1'); | ||
| addr.toByteArray(); // => [0x7f, 0, 0, 1] | ||
| ipaddr.IPv6.isIPv6('2001:db8::1'); // => true | ||
| ipaddr.IPv6.isIPv6('192.168.1.1'); // => false | ||
| ``` | ||
| or | ||
| <a name="ipv6-parse"></a> | ||
| #### `ipaddr.IPv6.parse(string)` | ||
| Parses the string and returns an `IPv6` object. Throws if the string is not a valid IPv6 address. | ||
| ```js | ||
| ipaddr.IPv6.parse('2001:db8::1').toString(); // => '2001:db8::1' | ||
| ipaddr.IPv6.parse('::ffff:192.168.1.1').toString(); // => '::ffff:c0a8:101' | ||
| ``` | ||
| <a name="ipv6-parsecidr"></a> | ||
| #### `ipaddr.IPv6.parseCIDR(string)` | ||
| Parses an IPv6 CIDR address and returns `[IPv6, prefixLength]`. Throws if the input is invalid. | ||
| ```js | ||
| const [addr, prefix] = ipaddr.IPv6.parseCIDR('2001:db8::/32'); | ||
| addr.toString(); // => '2001:db8::' | ||
| prefix; // => 32 | ||
| ``` | ||
| <a name="ipv6-broadcastaddressfromcidr"></a> | ||
| #### `ipaddr.IPv6.broadcastAddressFromCIDR(string)` | ||
| Returns the last address in the given IPv6 CIDR block (the IPv6 equivalent of the broadcast address). | ||
| ```js | ||
| ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8::/120').toString(); // => '2001:db8::ff' | ||
| ``` | ||
| <a name="ipv6-networkaddressfromcidr"></a> | ||
| #### `ipaddr.IPv6.networkAddressFromCIDR(string)` | ||
| Returns the network address for the given IPv6 CIDR block. | ||
| ```js | ||
| ipaddr.IPv6.networkAddressFromCIDR('2001:db8::42/32').toString(); // => '2001:db8::' | ||
| ``` | ||
| <a name="ipv6-subnetmaskfromprefixlength"></a> | ||
| #### `ipaddr.IPv6.subnetMaskFromPrefixLength(prefix)` | ||
| Returns the IPv6 subnet mask corresponding to the given CIDR prefix length. | ||
| ```js | ||
| ipaddr.IPv6.subnetMaskFromPrefixLength(64).toString(); // => 'ffff:ffff:ffff:ffff::' | ||
| ipaddr.IPv6.subnetMaskFromPrefixLength(48).toString(); // => 'ffff:ffff:ffff::' | ||
| ``` | ||
| ### Instance properties and methods | ||
| <a name="ipv6-parts"></a> | ||
| #### `addr.parts` | ||
| The eight 16-bit groups of the address as an array of numbers. | ||
| ```js | ||
| ipaddr.parse('2001:db8:10::1234:dead').parts; | ||
| // => [0x2001, 0x0db8, 0x0010, 0, 0, 0, 0x1234, 0xdead] | ||
| ``` | ||
| <a name="ipv6-zoneid"></a> | ||
| #### `addr.zoneId` | ||
| The zone ID string for link-local addresses, or `undefined` if absent. | ||
| ```js | ||
| ipaddr.parse('fe80::1%eth0').zoneId; // => 'eth0' | ||
| ipaddr.parse('2001:db8::1').zoneId; // => undefined | ||
| ``` | ||
| <a name="ipv6-kind"></a> | ||
| #### `addr.kind()` | ||
| Always returns `'ipv6'`. | ||
| ```js | ||
| ipaddr.parse('2001:db8::1').kind(); // => 'ipv6' | ||
| ``` | ||
| <a name="ipv6-isipv4mappedaddress"></a> | ||
| #### `addr.isIPv4MappedAddress()` | ||
| Returns `true` if this is an IPv4-mapped IPv6 address (i.e. in the `::ffff:0:0/96` range). | ||
| ```js | ||
| ipaddr.parse('::ffff:192.168.1.1').isIPv4MappedAddress(); // => true | ||
| ipaddr.parse('2001:db8::1').isIPv4MappedAddress(); // => false | ||
| ``` | ||
| <a name="ipv6-match"></a> | ||
| #### `addr.match(other, cidrBits)` / `addr.match([address, cidrBits])` | ||
| Returns `true` if the address falls within the given CIDR range. | ||
| ```js | ||
| const addr = ipaddr.parse('2001:db8:1234::1'); | ||
| addr.match(ipaddr.parse('2001:db8::'), 32); // => true | ||
| addr.match(ipaddr.parseCIDR('2001:db8::/32')); // => true | ||
| addr.match(ipaddr.parseCIDR('2001:db9::/32')); // => false | ||
| ``` | ||
| <a name="ipv6-range"></a> | ||
| #### `addr.range()` | ||
| Returns the name of the built-in range the address belongs to, or `'unicast'` if it does not match any. Recognised names include: `'unspecified'`, `'linkLocal'`, `'multicast'`, `'loopback'`, `'uniqueLocal'`, `'ipv4Mapped'`, `'reserved'`, and others. | ||
| See the [source][IPv6 ranges] for the full list. | ||
| ```js | ||
| ipaddr.parse('::1').range(); // => 'loopback' | ||
| ipaddr.parse('fe80::1').range(); // => 'linkLocal' | ||
| ipaddr.parse('fc00::1').range(); // => 'uniqueLocal' | ||
| ipaddr.parse('2001:db8::1').range(); // => 'reserved' | ||
| ipaddr.parse('2607:f8b0::1').range(); // => 'unicast' | ||
| ``` | ||
| <a name="ipv6-prefixlengthfromsubnetmask"></a> | ||
| #### `addr.prefixLengthFromSubnetMask()` | ||
| Returns the CIDR prefix length if this address is a valid contiguous subnet mask, or `null` otherwise. | ||
| ```js | ||
| ipaddr.parse('ffff:ffff:ffff:ffff::').prefixLengthFromSubnetMask(); // => 64 | ||
| ipaddr.parse('ffff:ffff::').prefixLengthFromSubnetMask(); // => 32 | ||
| ``` | ||
| <a name="ipv6-subnetmatch-instance"></a> | ||
| #### `addr.subnetMatch(rangeList[, defaultName])` | ||
| Instance-method shorthand for `ipaddr.subnetMatch(addr, rangeList, defaultName)`. | ||
| ```js | ||
| const addr = ipaddr.parse('2001:db8::1'); | ||
| addr.toByteArray(); // => [0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] | ||
| const ranges = { documentation: [ipaddr.parse('2001:db8::'), 32] }; | ||
| addr.subnetMatch(ranges); // => 'documentation' | ||
| ``` | ||
| <a name="ipv6-tobytearray"></a> | ||
| #### `addr.toByteArray()` | ||
| Returns the address as an array of 16 bytes in network byte order. | ||
| ```js | ||
| ipaddr.parse('2001:db8::1').toByteArray(); | ||
| // => [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] | ||
| ``` | ||
| <a name="ipv6-tofixedlengthstring"></a> | ||
| #### `addr.toFixedLengthString()` | ||
| Returns the address with all eight groups fully expanded to four hex digits, separated by colons. No `::` abbreviation is used. | ||
| ```js | ||
| ipaddr.parse('2001:db8::1').toFixedLengthString(); | ||
| // => '2001:0db8:0000:0000:0000:0000:0000:0001' | ||
| ``` | ||
| <a name="ipv6-toipv4address"></a> | ||
| #### `addr.toIPv4Address()` | ||
| Converts an IPv4-mapped IPv6 address to its IPv4 equivalent. Throws if the address is not IPv4-mapped. | ||
| ```js | ||
| ipaddr.parse('::ffff:192.168.1.1').toIPv4Address().toString(); // => '192.168.1.1' | ||
| ``` | ||
| <a name="ipv6-tonormalizedstring"></a> | ||
| #### `addr.toNormalizedString()` | ||
| Returns the address with all eight 16-bit groups written out in lowercase hex, separated by colons, without `::` compression. | ||
| ```js | ||
| ipaddr.parse('2001:db8::1').toNormalizedString(); // => '2001:db8:0:0:0:0:0:1' | ||
| ``` | ||
| <a name="ipv6-torfc5952string"></a> | ||
| #### `addr.toRFC5952String()` | ||
| Returns the address in the canonical format defined by [RFC 5952]: lowercase hex, leading zeros omitted, and the longest run of consecutive all-zero groups replaced by `::`. | ||
| ```js | ||
| ipaddr.parse('2001:0db8:0000:0000:0000:0000:0000:0001').toRFC5952String(); | ||
| // => '2001:db8::1' | ||
| ``` | ||
| <a name="ipv6-tostring"></a> | ||
| #### `addr.toString()` | ||
| Returns the compact string representation of the address, identical to `toRFC5952String()`. | ||
| ```js | ||
| ipaddr.parse('2001:0db8::0001').toString(); // => '2001:db8::1' | ||
| ``` | ||
| --- | ||
| [IPv4 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L184 | ||
| [IPv6 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L562 | ||
| [RFC 5952]: https://datatracker.ietf.org/doc/html/rfc5952 | ||
| [nodejs]: http://nodejs.org | ||
| [ci-url]: https://github.com/whitequark/ipaddr.js/actions?query=workflow%3A%22CI+Tests%22 | ||
| [ci-img]: https://github.com/whitequark/ipaddr.js/workflows/CI%20Tests/badge.svg |
| !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;t<r.length;t++)e.push(parseInt(r[t],16));return e}(),zoneId:a}}function p(t,r,e,n){if(t.length!==r.length)throw new Error("ipaddr: cannot match CIDR for objects with different lengths");let i,o=0;for(;n>0;){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<r;)t=`0${t}`;return t}const c={};c.IPv4=function(){function t(t){if(4!==t.length)throw new Error("ipaddr: ipv4 octet count should be 4");let r,e;for(r=0;r<t.length;r++)if(!(0<=(e=t[r])&&e<=255))throw new Error("ipaddr: ipv4 octet should fit in 8 bits");this.octets=t}return t.prototype.SpecialRanges={unspecified:[[new t([0,0,0,0]),8]],broadcast:[[new t([255,255,255,255]),32]],multicast:[[new t([224,0,0,0]),4]],linkLocal:[[new t([169,254,0,0]),16]],loopback:[[new t([127,0,0,0]),8]],carrierGradeNat:[[new t([100,64,0,0]),10]],private:[[new t([10,0,0,0]),8],[new t([172,16,0,0]),12],[new t([192,168,0,0]),16]],reserved:[[new t([192,0,0,0]),24],[new t([192,0,2,0]),24],[new t([192,88,99,0]),24],[new t([198,18,0,0]),15],[new t([198,51,100,0]),24],[new t([203,0,113,0]),24],[new t([240,0,0,0]),4]],as112:[[new t([192,175,48,0]),24],[new t([192,31,196,0]),24]],amt:[[new t([192,52,193,0]),24]]},t.prototype.kind=function(){return"ipv4"},t.prototype.match=function(t,r){let e;if(void 0===r&&(t=(e=t)[0],r=e[1]),"ipv4"!==t.kind())throw new Error("ipaddr: cannot match ipv4 address with non-ipv4 one");return p(this.octets,t.octets,8,r)},t.prototype.prefixLengthFromSubnetMask=function(){let t=0,r=!1;const e={0:8,128:7,192:6,224:5,240:4,248:3,252:2,254:1,255:0};let n,i,o;for(n=3;n>=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;r<t.length;r++)n=t[r],e.push(u(n));return e}();if(r=t.match(e.longValue)){if((i=u(r[1]))>4294967295||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<n;)r[e]=255,e++;return n<4&&(r[n]=Math.pow(2,t%8)-1<<8-t%8),new this(r)},c.IPv6=function(){function t(t,r){let e,n;if(16===t.length)for(this.parts=[],e=0;e<=14;e+=2)this.parts.push(t[e]<<8|t[e+1]);else{if(8!==t.length)throw new Error("ipaddr: ipv6 part count should be 8 or 16");this.parts=t}for(e=0;e<this.parts.length;e++)if(!(0<=(n=this.parts[e])&&n<=65535))throw new Error("ipaddr: ipv6 part should fit in 16 bits");r&&(this.zoneId=r)}return t.prototype.SpecialRanges={unspecified:[new t([0,0,0,0,0,0,0,0]),128],linkLocal:[new t([65152,0,0,0,0,0,0,0]),10],multicast:[new t([65280,0,0,0,0,0,0,0]),8],loopback:[new t([0,0,0,0,0,0,0,1]),128],uniqueLocal:[new t([64512,0,0,0,0,0,0,0]),7],ipv4Mapped:[new t([0,0,0,0,0,65535,0,0]),96],deprecatedSiteLocal:[new t([65216,0,0,0,0,0,0,0]),10],discard:[new t([256,0,0,0,0,0,0,0]),64],rfc6145:[new t([0,0,0,0,65535,0,0,0]),96],rfc6052:[[new t([100,65435,0,0,0,0,0,0]),96],[new t([100,65435,1,0,0,0,0,0]),48]],"6to4":[new t([8194,0,0,0,0,0,0,0]),16],teredo:[new t([8193,0,0,0,0,0,0,0]),32],benchmarking:[new t([8193,2,0,0,0,0,0,0]),48],amt:[new t([8193,3,0,0,0,0,0,0]),32],as112v6:[[new t([8193,4,274,0,0,0,0,0]),48],[new t([9760,79,32768,0,0,0,0,0]),48]],deprecatedOrchid:[new t([8193,16,0,0,0,0,0,0]),28],orchid2:[new t([8193,32,0,0,0,0,0,0]),28],droneRemoteIdProtocolEntityTags:[new t([8193,48,0,0,0,0,0,0]),28],segmentRouting:[new t([24320,0,0,0,0,0,0,0]),16],reserved:[[new t([8193,0,0,0,0,0,0,0]),23],[new t([8193,3512,0,0,0,0,0,0]),32],[new t([16383,0,0,0,0,0,0,0]),20]]},t.prototype.isIPv4MappedAddress=function(){return"ipv4Mapped"===this.range()},t.prototype.kind=function(){return"ipv6"},t.prototype.match=function(t,r){let e;if(void 0===r&&(t=(e=t)[0],r=e[1]),"ipv6"!==t.kind())throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one");return p(this.parts,t.parts,16,r)},t.prototype.prefixLengthFromSubnetMask=function(){let t=0,r=!1;const e={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 n,i;for(let o=7;o>=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<e.length;n++)t=e[n],r.push(t>>8),r.push(255&t);return r},t.prototype.toFixedLengthString=function(){const t=function(){const t=[];for(let r=0;r<this.parts.length;r++)t.push(d(this.parts[r].toString(16),4));return t}.call(this).join(":");let r="";return this.zoneId&&(r=`%${this.zoneId}`),t+r},t.prototype.toIPv4Address=function(){if(!this.isIPv4MappedAddress())throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4");const t=this.parts.slice(-2),r=t[0],e=t[1];return new c.IPv4([r>>8,255&r,e>>8,255&e])},t.prototype.toNormalizedString=function(){const t=function(){const t=[];for(let r=0;r<this.parts.length;r++)t.push(this.parts[r].toString(16));return t}.call(this).join(":");let r="";return this.zoneId&&(r=`%${this.zoneId}`),t+r},t.prototype.toRFC5952String=function(){const t=/((^|:)(0(:|$)){2,})/g,r=this.toNormalizedString();let e,n=0,i=-1;for(;e=t.exec(r);)e[0].length>i&&(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;e<o.length;e++)if(!(0<=(i=o[e])&&i<=255))return null;return r.parts.push(o[0]<<8|o[1]),r.parts.push(o[2]<<8|o[3]),{parts:r.parts,zoneId:r.zoneId}}return null},c.IPv6.subnetMaskFromPrefixLength=function(t){if((t=parseInt(t))<0||t>128)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<n;)r[e]=255,e++;return n<16&&(r[n]=Math.pow(2,t%8)-1<<8-t%8),new this(r)},c.fromByteArray=function(t){const r=t.length;if(4===r)return new c.IPv4(t);if(16===r)return new c.IPv6(t);throw new Error("ipaddr: the binary input is neither an IPv6 nor IPv4 address")},c.isValid=function(t){return c.IPv6.isValid(t)||c.IPv4.isValid(t)},c.isValidCIDR=function(t){return c.IPv6.isValidCIDR(t)||c.IPv4.isValidCIDR(t)},c.parse=function(t){if(c.IPv6.isValid(t))return c.IPv6.parse(t);if(c.IPv4.isValid(t))return c.IPv4.parse(t);throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format")},c.parseCIDR=function(t){try{return c.IPv6.parseCIDR(t)}catch(r){try{return c.IPv4.parseCIDR(t)}catch(t){throw new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format")}}},c.process=function(t){const r=this.parse(t);return"ipv6"===r.kind()&&r.isIPv4MappedAddress()?r.toIPv4Address():r},c.subnetMatch=function(t,r,e){let n,i,o,s;for(i in void 0!==e&&null!==e||(e="unicast"),r)if(Object.prototype.hasOwnProperty.call(r,i))for(!(o=r[i])[0]||o[0]instanceof Array||(o=[o]),n=0;n<o.length;n++)if(s=o[n],t.kind()===s[0].kind()&&t.match.apply(t,s))return i;return e},"undefined"!=typeof module&&module.exports?module.exports=c:t.ipaddr=c}(this)}("undefined"==typeof window?window={}:window); |
| 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<T> { | ||
| [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<IPv4 | IPv6>, 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<IPv4>, 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<IPv6>, defaultName?: string): string; | ||
| toIPv4Address(): IPv4; | ||
| toRFC5952String(): string; | ||
| } | ||
| } | ||
| export = Address; | ||
| } |
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
64424
1.05%992
1.64%1
-50%633
175.22%0
-100%3
50%5
-16.67%