ip-num
Version:
A TypeScript library for working with IPv4, IPv6 and ASN numbers. It provides representations of these internet protocol numbers with the ability to perform various IP related operations like parsing, validating etc. on them
1,217 lines • 46.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IPv6Mask = exports.IPv4Mask = exports.IPv6 = exports.Asn = exports.IPv4 = exports.AbstractIPNum = void 0;
exports.isIPv4 = isIPv4;
const Octet_1 = require("./Octet");
const Validator_1 = require("./Validator");
const BinaryUtils_1 = require("./BinaryUtils");
const BinaryUtils_2 = require("./BinaryUtils");
const BinaryUtils_3 = require("./BinaryUtils");
const BinaryUtils_4 = require("./BinaryUtils");
const Hexadecatet_1 = require("./Hexadecatet");
const HexadecimalUtils_1 = require("./HexadecimalUtils");
const IPv6Utils_1 = require("./IPv6Utils");
const HexadecimalUtils_2 = require("./HexadecimalUtils");
const IPRange_1 = require("./IPRange");
const IPRange_2 = require("./IPRange");
/**
* Provides the implementation of functionality that are common
* to {@link IPv4}, {@link IPv6}, {@link IPv4Mask} and {@link IPv6Mask}
*/
class AbstractIPNum {
/**
* Gets the numeric value of an IP number as {@link BigInt}
*
* @returns bigInt the numeric value of an IP number.
*/
getValue() {
return this.value;
}
/**
* Gets the binary string representation of an IP number.
*
* @returns {string} the string binary representation.
*/
toBinaryString() {
return (0, BinaryUtils_3.leftPadWithZeroBit)(this.value.toString(2), this.bitSize);
}
/**
* Checks if an IP number has a value greater than the present value
* @returns {boolean} true, if there is a value greater than the present value. Returns false otherwise.
*/
hasNext() {
return this.value < this.maximumBitSize;
}
/**
* Checks if an IP number has a value lesser than the present value
* @returns {boolean} true, if there is a value lesser than the present value. Returns false otherwise.
*/
hasPrevious() {
return this.value > 0n;
}
/**
* Checks if the given IP number, is equals to the current IP number
*
* @param {AbstractIPNum} anotherIPNum the other IP number to compare with
* @returns {boolean} true if the given IP number is equals
*/
isEquals(anotherIPNum) {
return this.value === anotherIPNum.value;
}
/**
* Checks if the given IP number is lesser than this current IP number
*
* @param {AbstractIPNum} anotherIPNum the other IP number to compare with
* @returns {boolean} true if the given IP number is less than this current one. False otherwise.
*/
isLessThan(anotherIPNum) {
return this.value < anotherIPNum.value;
}
/**
* Checks if the given IP number is greater than this current IP number
*
* @param {AbstractIPNum} anotherIPNum the other IP number to compare with
* @returns {boolean} true if the given IP number is greater than this current one. False otherwise.
*/
isGreaterThan(anotherIPNum) {
return this.value > anotherIPNum.value;
}
/**
* Checks if the given IP number is less than or equals to this current IP number
*
* @param {AbstractIPNum} anotherIPNum the other IP number to compare with
* @returns {boolean} true if the given IP number is less than or equals to this current one. False otherwise.
*/
isLessThanOrEquals(anotherIPNum) {
return this.value <= anotherIPNum.value;
}
/**
* Checks if the given IP number is greater than or equals to this current IP number
*
* @param {AbstractIPNum} anotherIPNum the other IP number to compare with
* @returns {boolean} {boolean} true if the given IP number is greater than or equals to this current one. False
* otherwise.
*/
isGreaterThanOrEquals(anotherIPNum) {
return this.value >= anotherIPNum.value;
}
}
exports.AbstractIPNum = AbstractIPNum;
/**
* Represents an IPv4 number. A 32 bit number that is used to uniquely identify a device that is part of a computer
* network that uses the internet protocol for communication.
*
* @see https://en.wikipedia.org/wiki/IPv4
* @see https://www.rfc-editor.org/info/rfc791
*/
class IPv4 extends AbstractIPNum {
/**
* A convenience method for creating an {@link IPv4} by providing the decimal value of the IP number in BigInt
*
* @param {bigint} bigIntValue the decimal value of the IP number in BigInt
* @returns {IPv4} the IPv4 instance
*/
static fromNumber(bigIntValue) {
return new IPv4(bigIntValue);
}
/**
* A convenience method for creating an {@link IPv4} by providing the IP number in dot-decimal notation. E.g
* "10.1.1.10"
*
* {@see https://en.wikipedia.org/wiki/Dot-decimal_notation} for more information on dot-decimal notation.
*
* @param {string} ipString the IP number in dot-decimal notation
* @returns {IPv4} the IPv4 instance
*/
static fromDecimalDottedString(ipString) {
return new IPv4(ipString);
}
/**
* Alias for IPv4.fromDecimalDottedString.
*
* @param {string} ipString the IP number in dot-decimal notation
* @returns {IPv4} the IPv4 instance
*/
static fromString(ipString) {
return IPv4.fromDecimalDottedString(ipString);
}
/**
* A convenience method for creating an {@link IPv4} from binary string
*
* @param {string} ipBinaryString the binary string representing the IPv4 number to be created
* @returns {IPv4} the IPv4 instance
*/
static fromBinaryString(ipBinaryString) {
let validationResult = Validator_1.Validator.isValidBinaryString(ipBinaryString);
if (validationResult[0]) {
return new IPv4((0, BinaryUtils_2.parseBinaryStringToBigInt)(ipBinaryString));
}
else {
throw Error(validationResult[1].join(','));
}
}
/**
* Constructor for an IPv4 number.
*
* @param {string | bigint} ipValue value to construct an IPv4 from. The given value can either be
* numeric or string. If a string is given then it needs to be in dot-decimal notation
*/
constructor(ipValue) {
super();
/**
* The number of bits needed to represents the value of the IPv4 number
*/
this.bitSize = 32;
/**
* The maximum bit size (i.e. binary value) of the IPv4 number in BigInt
*/
this.maximumBitSize = Validator_1.Validator.THIRTY_TWO_BIT_SIZE;
/**
* The type of IP number. Value is one of the values of the {@link IPNumType} enum
* @type {IPNumType} the type of IP number
*/
this.type = "IPv4" /* IPNumType.IPv4 */;
/**
* An array of {@link Octet}'s
*
* @type {Array} the octets that makes up the IPv4 number
*/
this.octets = [];
/**
* The string character used to separate the individual octets when the IPv4 is rendered as strings
*
* @type {string} The string character used to separate the individual octets when rendered as strings
*/
this.separator = ".";
if (typeof ipValue === "string") {
let [value, octets] = this.constructFromDecimalDottedString(ipValue);
this.value = value;
this.octets = octets;
}
else {
let [value, octets] = this.constructFromBigIntValue(ipValue);
this.value = value;
this.octets = octets;
}
}
/**
* A string representation of the IPv4 number. The string representation is in dot-decimal notation
*
* @returns {string} The string representation in dot-decimal notation
*/
toString() {
return this.octets.map((value) => { return value.toString(); }).join(this.separator);
}
/**
* Gets the individual {@link Octet} that makes up the IPv4 number
*
* @returns {Array<Octet>} The individual {@link Octet} that makes up the IPv4 number
*/
getOctets() {
return this.octets;
}
/**
* Returns the next IPv4 number
*
* @returns {IPv4} the next IPv4 number
*/
nextIPNumber() {
return IPv4.fromNumber(this.getValue() + 1n);
}
/**
* Returns the previous IPv4 number
*
* @returns {IPv4} the previous IPv4 number
*/
previousIPNumber() {
return IPv4.fromNumber(this.getValue() - 1n);
}
/**
* Checks if this IPv4 address is a private address according to RFC 1918.
*
* Private IPv4 address ranges:
* - 10.0.0.0/8 (10.0.0.0 to 10.255.255.255)
* - 172.16.0.0/12 (172.16.0.0 to 172.31.255.255)
* - 192.168.0.0/16 (192.168.0.0 to 192.168.255.255)
*
* @see https://datatracker.ietf.org/doc/html/rfc1918
* @returns {boolean} true if this IPv4 address is private, false otherwise
*/
isPrivate() {
return IPv4.PRIVATE_RANGES.some(range => range.contains(this));
}
/**
* Checks if this IPv4 address is a documentation address according to RFC 5737.
*
* Documentation IPv4 address ranges:
* - 192.0.2.0/24 (TEST-NET-1)
* - 198.51.100.0/24 (TEST-NET-2)
* - 203.0.113.0/24 (TEST-NET-3)
*
* @see https://datatracker.ietf.org/doc/html/rfc5737
* @returns {boolean} true if this IPv4 address is reserved for documentation, false otherwise
*/
isDocumentation() {
return IPv4.DOCUMENTATION_RANGES.some(range => range.contains(this));
}
/**
* Checks if this IPv4 address is a multicast address according to RFC 1112.
*
* Multicast IPv4 address range:
* - 224.0.0.0/4 (224.0.0.0 to 239.255.255.255)
*
* @see https://datatracker.ietf.org/doc/html/rfc1112
* @returns {boolean} true if this IPv4 address is multicast, false otherwise
*/
isMulticast() {
return IPv4.MULTICAST_RANGE.contains(this);
}
/**
* Checks if this IPv4 address is a broadcast address.
*
* When called without arguments, checks for the limited broadcast address (255.255.255.255).
* When a subnet is provided, checks if this address is the directed broadcast for that subnet.
*
* @param subnet Optional CIDR range to check for directed broadcast
* @returns {boolean} true if this is a broadcast address, false otherwise
*/
isBroadcast(subnet) {
if (subnet) {
return this.isEquals(subnet.getLast());
}
return this.isEquals(IPv4.LIMITED_BROADCAST);
}
/**
* Checks if this IPv4 address is a loopback address according to RFC 5735.
*
* Loopback IPv4 address range:
* - 127.0.0.0/8 (127.0.0.0 to 127.255.255.255)
*
* @see https://datatracker.ietf.org/doc/html/rfc5735
* @returns {boolean} true if this IPv4 address is loopback, false otherwise
*/
isLoopback() {
return IPv4.LOOPBACK_RANGE.contains(this);
}
/**
* Checks if this IPv4 address is an unspecified address according to RFC 6890.
*
* Unspecified IPv4 address:
* - 0.0.0.0/32 (all zeros)
*
* @see https://datatracker.ietf.org/doc/html/rfc6890
* @returns {boolean} true if this IPv4 address is unspecified, false otherwise
*/
isUnspecified() {
return this.value === 0n;
}
/**
* Checks if this IPv4 address is a link-local address according to RFC 6890.
*
* Link-local IPv4 address range:
* - 169.254.0.0/16 (169.254.0.0 to 169.254.255.255)
*
* @see https://datatracker.ietf.org/doc/html/rfc6890
* @returns {boolean} true if this IPv4 address is link-local, false otherwise
*/
isLinkLocal() {
return IPv4.LINK_LOCAL_RANGE.contains(this);
}
/**
* Checks if this IPv4 address is a global unicast address.
*
* Global unicast is defined here as "everything else" - any address that does not
* match the other specific address types (Unspecified, Loopback, Private, Link-Local,
* Documentation, Multicast, Reserved, or Broadcast).
*
* Note that this is not a strict "publicly routable" check: it does not consult the
* full IANA special-purpose address registry (RFC 6890), so special-purpose ranges
* not covered by the checks above - such as shared address space 100.64.0.0/10
* (RFC 6598) and benchmarking space 198.18.0.0/15 (RFC 2544) - still return true.
*
* @see https://datatracker.ietf.org/doc/html/rfc6890
* @returns {boolean} true if this IPv4 address is global unicast, false otherwise
*/
isGlobalUnicast() {
// Global Unicast is "everything else" - not any of the other specific types
return !this.isUnspecified() &&
!this.isLoopback() &&
!this.isPrivate() &&
!this.isLinkLocal() &&
!this.isDocumentation() &&
!this.isMulticast() &&
!this.isReserved() &&
!this.isBroadcast();
}
/**
* Checks if this IPv4 address is in a reserved range according to RFC 6890.
*
* Reserved IPv4 address range:
* - 240.0.0.0/4 (240.0.0.0 to 255.255.255.254)
*
* Note: 255.255.255.255 is the limited broadcast address, not reserved.
*
* @see https://datatracker.ietf.org/doc/html/rfc6890
* @returns {boolean} true if this IPv4 address is reserved, false otherwise
*/
isReserved() {
// Reserved range is 240.0.0.0/4, but exclude 255.255.255.255 (broadcast)
const reservedStart = IPv4.fromDecimalDottedString("240.0.0.0").value;
const reservedEnd = IPv4.fromDecimalDottedString("255.255.255.254").value;
return this.value >= reservedStart && this.value <= reservedEnd;
}
/**
* Returns this IPv4 number as a IPv4-Mapped IPv6 Address
*
* The IPv4-Mapped IPv6 Address allows an IPv4 number to be embedded within an IPv6 number
*
* {@see https://tools.ietf.org/html/rfc4291#section-2.5.5} for more information on the IPv4-Mapped IPv6 Address
*
* @returns {IPv6} an IPv6 number with the IPv4 embedded within it
*/
toIPv4MappedIPv6() {
let binary = '1'.repeat(16) + this.toBinaryString();
return IPv6.fromBinaryString(binary);
}
constructFromDecimalDottedString(ipString) {
let octets;
let value;
let [isValid, message] = Validator_1.Validator.isValidIPv4String(ipString);
if (!isValid) {
throw new Error(message.filter(msg => { return msg !== ''; }).toString());
}
let stringOctets = ipString.split(".");
octets = stringOctets.map((rawOctet) => {
return Octet_1.Octet.fromString(rawOctet);
});
value = BigInt(`0b${(0, BinaryUtils_1.dottedDecimalNotationToBinaryString)(ipString)}`);
return [value, octets];
}
constructFromBigIntValue(ipv4Number) {
let [isValid, message] = Validator_1.Validator.isValidIPv4Number(ipv4Number);
if (!isValid) {
throw new Error(message.filter(msg => { return msg !== ''; }).toString());
}
let binaryString = (0, BinaryUtils_4.numberToBinaryString)(ipv4Number);
ipv4Number = typeof ipv4Number === "bigint" ? ipv4Number : BigInt(ipv4Number);
return [ipv4Number, this.binaryStringToDecimalOctets(binaryString)];
}
binaryStringToDecimalOctets(ipv4BinaryString) {
if (ipv4BinaryString.length < 32) {
ipv4BinaryString = (0, BinaryUtils_3.leftPadWithZeroBit)(ipv4BinaryString, 32);
}
let octets = ipv4BinaryString.match(/.{1,8}/g);
return octets.map((octet) => {
return Octet_1.Octet.fromString((0, BinaryUtils_2.parseBinaryStringToBigInt)(octet).toString());
});
}
}
exports.IPv4 = IPv4;
/**
* RFC 1918 private address ranges. These ranges are constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc1918
*/
IPv4.PRIVATE_RANGES = [
IPRange_1.IPv4CidrRange.fromCidr("10.0.0.0/8"),
IPRange_1.IPv4CidrRange.fromCidr("172.16.0.0/12"),
IPRange_1.IPv4CidrRange.fromCidr("192.168.0.0/16")
];
/**
* RFC 5737 documentation address ranges. These ranges are constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc5737
*/
IPv4.DOCUMENTATION_RANGES = [
IPRange_1.IPv4CidrRange.fromCidr("192.0.2.0/24"),
IPRange_1.IPv4CidrRange.fromCidr("198.51.100.0/24"),
IPRange_1.IPv4CidrRange.fromCidr("203.0.113.0/24")
];
/**
* RFC 1112 multicast address range. This range is constant and reused for performance.
*
* Multicast IPv4 address range:
* - 224.0.0.0/4 (224.0.0.0 to 239.255.255.255)
*
* @see https://datatracker.ietf.org/doc/html/rfc1112
*/
IPv4.MULTICAST_RANGE = IPRange_1.IPv4CidrRange.fromCidr("224.0.0.0/4");
/**
* RFC 5735 loopback address range. This range is constant and reused for performance.
*
* Loopback IPv4 address range:
* - 127.0.0.0/8 (127.0.0.0 to 127.255.255.255)
*
* @see https://datatracker.ietf.org/doc/html/rfc5735
*/
IPv4.LOOPBACK_RANGE = IPRange_1.IPv4CidrRange.fromCidr("127.0.0.0/8");
/**
* RFC 6890 link-local address range. This range is constant and reused for performance.
*
* Link-local IPv4 address range:
* - 169.254.0.0/16 (169.254.0.0 to 169.254.255.255)
*
* @see https://datatracker.ietf.org/doc/html/rfc6890
*/
IPv4.LINK_LOCAL_RANGE = IPRange_1.IPv4CidrRange.fromCidr("169.254.0.0/16");
/**
* The limited broadcast address (255.255.255.255). This is constant and reused for performance.
*/
IPv4.LIMITED_BROADCAST = IPv4.fromDecimalDottedString("255.255.255.255");
/**
* Represents an Autonomous System Number. Which is a number that is used to identify
* a group of IP addresses with a common, clearly defined routing policy.
*
* @see https://en.wikipedia.org/wiki/Autonomous_system_(Internet)
* @see https://tools.ietf.org/html/rfc5396
*/
class Asn extends AbstractIPNum {
/**
* A convenience method for creating an instance of {@link Asn} from a string
*
* The given string can be in asplain, asdot or asdot+ representation format.
* {@see https://tools.ietf.org/html/rfc5396} for more information on
* the different ASN string representation
*
* @param {string} rawValue the asn string. In either asplain, asdot or asdot+ format
* @returns {Asn} the constructed ASN instance
*/
static fromString(rawValue) {
return new Asn(rawValue);
}
;
/**
* A convenience method for creating an instance of {@link Asn} from a numeric value
*
* @param {number} rawValue the asn numeric value
* @returns {Asn} the constructed ASN instance
*/
static fromNumber(rawValue) {
return new Asn(rawValue);
}
;
/**
* A convenience method for creating an instance of {@link Asn} from a binary string
*
* @param {string} binaryString to create an ASN instance from
* @returns {Asn} the constructed ASN instance
*/
static fromBinaryString(binaryString) {
let validationResult = Validator_1.Validator.isValidBinaryString(binaryString);
if (validationResult[0]) {
return new Asn(parseInt(binaryString, 2));
}
else {
throw Error(validationResult[1].join(','));
}
}
/**
* Constructor for an instance of {@link ASN}
*
* @param {string | number} rawValue value to construct an ASN from. The given value can either be numeric or
* string. If in string then it can be in asplain, asdot or asdot+ string representation format
*/
constructor(rawValue) {
super();
/**
* The number of bits needed to represents the value of the ASN number
*/
this.bitSize = 32;
/**
* The maximum bit size (i.e. binary value) of the ASN number in BigInt
*/
this.maximumBitSize = Validator_1.Validator.THIRTY_TWO_BIT_SIZE;
this.type = "ASN" /* IPNumType.ASN */;
if (typeof rawValue === 'string') {
if (Asn.startWithASPrefix(rawValue)) {
this.value = BigInt(parseInt(rawValue.substring(2)));
}
else if (rawValue.indexOf(".") != -1) {
this.value = BigInt(this.parseFromDotNotation(rawValue));
}
else {
this.value = BigInt(parseInt(rawValue));
}
}
else {
let valueAsBigInt = BigInt(rawValue);
let [isValid, message] = Validator_1.Validator.isValidAsnNumber(valueAsBigInt);
if (!isValid) {
throw Error(message.filter(msg => { return msg !== ''; }).toString());
}
this.value = valueAsBigInt;
}
}
/**
* A string representation where the asn value is prefixed by "ASN". For example "AS65526"
*
* @returns {string} A string representation where the asn value is prefixed by "ASN"
*/
toString() {
let stringValue = this.value.toString();
return `${Asn.AS_PREFIX}${stringValue}`;
}
/**
* A string representation where the ASN numeric value of is represented as a string. For example "65526"
*
* @returns {string} A string representation where the ASN numeric value of is represented as a string
*/
toASPlain() {
return this.value.toString();
}
/**
* A string representation where the ASN value is represented using the asplain notation if the ASN value is
* less than 65536 and uses asdot+ notation when the value is greater than 65536.
*
* For example 65526 will be represented as "65526" while 65546 will be represented as "1.10"
*
*
* @returns {string} A string representation of the ASN in either asplain or asdot+ notation depending on
* whether the numeric value of the ASN number is greater than 65526 or not.
*/
toASDot() {
if (this.value.valueOf() >= 65536n) {
return this.toASDotPlus();
}
return this.toASPlain();
}
/**
* A string representation where the ASN value is represented using the asdot+ notation
*
* @returns {string} A string representation where the ASN value is represented using the asdot+ notation
*
*/
toASDotPlus() {
let high = this.value.valueOf() / 65536n;
let low = this.value.valueOf() % 65536n;
return `${high}.${low}`;
}
/**
* Converts the ASN value to binary numbers represented with strings
*
* @returns {string} a binary string representation of the value of the ASN number
*/
toBinaryString() {
return (0, BinaryUtils_4.numberToBinaryString)(this.value);
}
/**
* Checks if the ASN value is 16bit
*
* @returns {boolean} true if the ASN is a 16bit value. False otherwise.
*/
is16Bit() {
let [valid16BitAsnNumber,] = Validator_1.Validator.isValid16BitAsnNumber(this.value);
return valid16BitAsnNumber;
}
/**
* Checks if the ASN value is 32bit
*
* @returns {boolean} true if the ASN is a 32bit value. False otherwise.
*/
is32Bit() {
return !this.is16Bit();
}
/**
* Returns the next ASN number
*
* @returns {AbstractIPNum} the next ASN number
*/
nextIPNumber() {
return new Asn(this.value.valueOf() + 1n);
}
/**
* Returns the previous ASN number
*
* @returns {AbstractIPNum} the previous ASN number
*/
previousIPNumber() {
return new Asn(this.value.valueOf() - 1n);
}
/**
* Checks if this ASN is a multicast address.
*
* ASNs are not IP addresses, so this always returns false.
*
* @returns {boolean} always returns false for ASN
*/
isMulticast() {
return false;
}
/**
* Checks if this ASN is a private address.
*
* ASNs are not IP addresses, so this always returns false.
*
* @returns {boolean} always returns false for ASN
*/
isPrivate() {
return false;
}
static startWithASPrefix(word) {
return word.indexOf(Asn.AS_PREFIX) === 0;
}
parseFromDotNotation(rawValue) {
let values = rawValue.split(".");
let high = parseInt(values[0]);
let low = parseInt(values[1]);
return (high * 65535) + (low + high);
}
}
exports.Asn = Asn;
Asn.AS_PREFIX = "AS";
/**
* Represents an IPv6 number. A 128 bit number that is used to uniquely identify a device that is part of a computer
* network that uses the internet protocol for communication.
*
* @see https://en.wikipedia.org/wiki/IPv6
* @see https://www.rfc-editor.org/info/rfc8200
*/
class IPv6 extends AbstractIPNum {
/**
* A convenience method for creating an {@link IPv6} by providing the decimal value of the IP number in BigInt
*
* @param {bigint} bigIntValue the decimal value of the IP number in BigInt
* @returns {IPv6} the IPv6 instance
*/
static fromBigInt(bigIntValue) {
return new IPv6(bigIntValue);
}
/**
* A convenience method for creating an {@link IPv6} by providing the IP number in hexadecatet notation. E.g
* "2001:800:0:0:0:0:0:2002"
*
* {@see https://en.wikipedia.org/wiki/IPv6_address#Representation} for more information on hexadecatet notation.
*
* @param {string} ipString the IP number in hexadecatet
* @returns {IPv6} the IPv6 instance
*/
static fromHexadecatet(ipString) {
return new IPv6(ipString);
}
/**
* Alias for IPv6.fromHexadecimalString
*
* @param {string} ipString the IP number in hexadecatet
* @returns {IPv6} the IPv6 instance
*/
static fromString(ipString) {
return IPv6.fromHexadecatet(ipString);
}
/**
* A convenience method for creating an {@link IPv6} from binary string
*
* @param {string} ipBinaryString the binary string representing the IPv6 number to be created
* @returns {IPv6} the IPv6 instance
*/
static fromBinaryString(ipBinaryString) {
let validationResult = Validator_1.Validator.isValidBinaryString(ipBinaryString);
if (validationResult[0]) {
let paddedBinaryString = (0, BinaryUtils_3.leftPadWithZeroBit)(ipBinaryString, 128);
return new IPv6((0, BinaryUtils_2.parseBinaryStringToBigInt)(paddedBinaryString));
}
else {
throw Error(validationResult[1].join(','));
}
}
/**
* A convenience method for creating an IPv4-Compatible {@link IPv6} Address from an instance of {@link IPv4}
*
* @param {IPv4} ipv4 to create an IPv4-Compatible {@link IPv6} Address
* @returns {IPv6} the IPv4-Compatible {@link IPv6} Address
*/
static fromIPv4(ipv4) {
return ipv4.toIPv4MappedIPv6();
}
/**
* A convenience method for creating an IPv4-Compatible {@link IPv6} Address from a IPv4 represented in
* dot-decimal notation i.e. 127.0.0.1
*
* @param {IPv4} ip4DotDecimalString string represented in a dot decimal string
* @returns {IPv6} the IPv4-Compatible {@link IPv6} Address
*/
static fromIPv4DotDecimalString(ip4DotDecimalString) {
return new IPv4(ip4DotDecimalString).toIPv4MappedIPv6();
}
/**
* Constructor for an IPv6 number.
*
* @param {string | bigint} ipValue value to construct an IPv6 from. The given value can either be
* numeric or string. If a string is given then it needs to be in hexadecatet string notation
*/
constructor(ipValue, zoneId) {
super();
/**
* The number of bits needed to represents the value of the IPv6 number
*/
this.bitSize = 128;
/**
* The maximum bit size (i.e. binary value) of the IPv6 number in BigInt
*/
this.maximumBitSize = Validator_1.Validator.ONE_HUNDRED_AND_TWENTY_EIGHT_BIT_SIZE;
/**
* The type of IP number. Value is one of the values of the {@link IPNumType} enum
* @type {IPNumType} the type of IP number
*/
this.type = "IPv6" /* IPNumType.IPv6 */;
/**
* An array of {@link Hexadecatet}'s
*
* @type {Array} the hexadecatet that makes up the IPv6 number
*/
this.hexadecatet = [];
/**
* The string character used to separate the individual hexadecatet when the IPv6 is rendered as strings
*
* @type {string} The string character used to separate the individual hexadecatet when rendered as strings
*/
this.separator = ":";
if (typeof ipValue === "string") {
let [isValid, message] = Validator_1.Validator.isValidIPv6String(ipValue);
if (!isValid) {
throw new Error(message.filter(msg => { return msg !== ''; }).toString());
}
let [ipv6, zId] = ipValue.split('%');
this.zoneId = zId ? zId : zoneId;
let expandedIPv6 = (0, IPv6Utils_1.expandIPv6Number)(ipv6);
let [value, hexadecatet] = this.constructFromHexadecimalDottedString(expandedIPv6);
this.value = value;
this.hexadecatet = hexadecatet;
}
else {
let [value, hexadecatet] = this.constructFromBigIntValue(ipValue);
this.value = value;
this.hexadecatet = hexadecatet;
this.zoneId = zoneId;
}
}
/**
* A string representation of the IPv6 number.
*
* @returns {string} The string representation of IPv6
*/
toString() {
let ipv6String = this.hexadecatet.map(v => v.toString()).join(":");
if (this.hexadecatet.length < 8) {
ipv6String = `::${ipv6String}`;
}
if (this.zoneId) {
ipv6String = `${(0, IPv6Utils_1.collapseIPv6Number)(ipv6String)}%${this.zoneId}`;
}
return ipv6String;
}
/**
* Gets the individual {@link Hexadecatet} that makes up the IPv6 number
*
* @returns {Array<Hexadecatet>} The individual {@link Hexadecatet} that makes up the IPv6 number
*/
//TODO maybe rename to something like getSegments? so it can be same with getOctet
getHexadecatet() {
return this.hexadecatet;
}
/**
* Returns the next IPv6 number
*
* @returns {IPv6} the next IPv6 number
*/
nextIPNumber() {
return IPv6.fromBigInt(this.getValue() + 1n);
}
/**
* Returns the previous IPv6 number
*
* @returns {IPv6} the previous IPv6 number
*/
previousIPNumber() {
return IPv6.fromBigInt(this.getValue() - 1n);
}
/**
* Checks if this IPv6 address is a private address according to RFC 4193.
*
* Private IPv6 address range:
* - fc00::/7 (Unique Local Addresses)
*
* @see https://datatracker.ietf.org/doc/html/rfc4193
* @returns {boolean} true if this IPv6 address is private, false otherwise
*/
isPrivate() {
return IPv6.PRIVATE_RANGE.contains(this);
}
/**
* Checks if this IPv6 address is a documentation address according to RFC 3849 and RFC 9637.
*
* Documentation IPv6 address ranges:
* - 2001:db8::/32 (RFC 3849)
* - 3fff::/20 (RFC 9637)
*
* @see https://datatracker.ietf.org/doc/html/rfc3849
* @see https://datatracker.ietf.org/doc/html/rfc9637
* @returns {boolean} true if this IPv6 address is reserved for documentation, false otherwise
*/
isDocumentation() {
return IPv6.DOCUMENTATION_RANGES.some(range => range.contains(this));
}
/**
* Checks if this IPv6 address is a multicast address.
*
* Multicast IPv6 address range:
* - ff00::/8 (ff00:: to ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff)
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
* @returns {boolean} true if this IPv6 address is multicast, false otherwise
*/
isMulticast() {
return IPv6.MULTICAST_RANGE.contains(this);
}
/**
* Checks if this IPv6 multicast address has an embedded Rendezvous Point (RP).
*
* For an embedded RP to be present, the R, P, and T flags must all be set to 1.
* The R flag (bit 9) indicates RP embedded, P flag (bit 10) indicates prefix-based,
* and T flag (bit 11) indicates transient address.
*
* @see https://datatracker.ietf.org/doc/html/rfc3956
* @returns {boolean} true if embedded RP is present (R, P, T flags all set), false otherwise
* @throws {Error} if this is not a multicast address
*/
hasEmbeddedRP() {
if (!this.isMulticast()) {
throw new Error("Embedded RP can only be checked for multicast addresses");
}
// Extract second octet (bits 8-15, 0-indexed)
// Shift right by 112 bits to get bits 8-15, then mask to get second octet
const secondOctet = Number((this.value >> 112n) & 0xffn);
// Check R flag (bit 1 of second octet = 0x40)
// Check P flag (bit 2 of second octet = 0x20)
// Check T flag (bit 3 of second octet = 0x10)
// All three flags must be set: (R & P & T) = 0x70
return (secondOctet & 0x70) === 0x70;
}
/**
* Checks if this IPv6 address is an unspecified address according to RFC 4291.
*
* Unspecified IPv6 address:
* - ::/128 (all zeros)
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
* @returns {boolean} true if this IPv6 address is unspecified, false otherwise
*/
isUnspecified() {
return this.value === 0n;
}
/**
* Checks if this IPv6 address is a loopback address according to RFC 4291.
*
* Loopback IPv6 address:
* - ::1/128
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
* @returns {boolean} true if this IPv6 address is loopback, false otherwise
*/
isLoopback() {
return this.value === 1n;
}
/**
* Checks if this IPv6 address is a link-local address according to RFC 4291.
*
* Link-local IPv6 address range:
* - fe80::/10 (fe80:: to febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff)
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
* @returns {boolean} true if this IPv6 address is link-local, false otherwise
*/
isLinkLocal() {
return IPv6.LINK_LOCAL_RANGE.contains(this);
}
/**
* Checks if this IPv6 address is a global unicast address according to RFC 4291.
*
* According to RFC 4291, global unicast addresses are defined as "everything else" -
* any address that does not match the other specific address types (Unspecified,
* Loopback, Multicast, Link-Local, IPv4-Mapped, Discard-Only, Documentation, or Private).
*
* Note that this is not a strict "publicly routable" check: it does not consult the
* full IANA special-purpose address registry, so special-purpose ranges not covered
* by the checks above still return true.
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
* @returns {boolean} true if this IPv6 address is global unicast, false otherwise
*/
isGlobalUnicast() {
// Global Unicast is "everything else" - not any of the other specific types
return !this.isUnspecified() &&
!this.isLoopback() &&
!this.isMulticast() &&
!this.isLinkLocal() &&
!this.isIPv4Mapped() &&
!this.isDiscardOnly() &&
!this.isDocumentation() &&
!this.isPrivate();
}
/**
* Checks if this IPv6 address is an IPv4-mapped IPv6 address according to RFC 4291.
*
* IPv4-mapped IPv6 addresses have a specific format:
* - First 80 bits: all zeros
* - Next 16 bits: 0xffff
* - Last 32 bits: IPv4 address
*
* This corresponds to the format ::ffff:x.x.x.x where x.x.x.x is an IPv4 address.
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
* @returns {boolean} true if this IPv6 address is IPv4-mapped, false otherwise
*/
isIPv4Mapped() {
// Top 96 bits must be 0x0000...0000ffff (80 zero bits followed by ffff)
return (this.value >> 32n) === 0xffffn;
}
/**
* Checks if this IPv6 address is a discard-only address according to RFC 6666.
*
* Discard-only IPv6 address range:
* - 100::/64 (100:: to 100::ffff:ffff:ffff:ffff)
*
* @see https://datatracker.ietf.org/doc/html/rfc6666
* @returns {boolean} true if this IPv6 address is discard-only, false otherwise
*/
isDiscardOnly() {
return IPv6.DISCARD_ONLY_RANGE.contains(this);
}
/**
* Gets the kind/category of this IPv6 address.
*
* Returns the most specific kind that matches this address. The check order ensures
* correct classification when address ranges overlap:
* 1. Unspecified (::)
* 2. Loopback (::1)
* 3. Multicast (ff00::/8)
* 4. Documentation (2001:db8::/32 and 3fff::/20)
* 5. IPv4-Mapped (::ffff:0:0/96)
* 6. Discard-Only (100::/64)
* 7. Link-Local (fe80::/10)
* 8. Unique Local Address/Private (fc00::/7)
* 9. Global Unicast (everything else, per RFC 4291)
* 10. Unknown (fallback for reserved/unassigned ranges)
*
* According to RFC 4291, Global Unicast addresses are defined as "everything else" -
* any address that does not match the other specific address types.
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
* @returns {IPv6AddressKind} the kind of this IPv6 address
*/
getKind() {
// Check in order of specificity (more specific first)
if (this.isUnspecified()) {
return "Unspecified" /* IPv6AddressKind.UNSPECIFIED */;
}
if (this.isLoopback()) {
return "Loopback" /* IPv6AddressKind.LOOPBACK */;
}
if (this.isMulticast()) {
return "Multicast" /* IPv6AddressKind.MULTICAST */;
}
if (this.isDocumentation()) {
return "Documentation" /* IPv6AddressKind.DOCUMENTATION */;
}
if (this.isIPv4Mapped()) {
return "IPv4-Mapped IPv6" /* IPv6AddressKind.IPV4_MAPPED */;
}
if (this.isDiscardOnly()) {
return "Discard-Only" /* IPv6AddressKind.DISCARD_ONLY */;
}
if (this.isLinkLocal()) {
return "Link-Local" /* IPv6AddressKind.LINK_LOCAL */;
}
if (this.isPrivate()) {
return "Unique Local Address" /* IPv6AddressKind.UNIQUE_LOCAL */;
}
if (this.isGlobalUnicast()) {
return "Global Unicast" /* IPv6AddressKind.GLOBAL_UNICAST */;
}
return "Unknown" /* IPv6AddressKind.UNKNOWN */;
}
constructFromBigIntValue(ipv6Number) {
let [isValid, message] = Validator_1.Validator.isValidIPv6Number(ipv6Number);
if (!isValid) {
throw new Error(message.filter(msg => { return msg !== ''; }).toString());
}
let binaryString = (0, BinaryUtils_4.numberToBinaryString)(ipv6Number);
return [ipv6Number, this.binaryStringToHexadecatets(binaryString)];
}
constructFromHexadecimalDottedString(expandedIPv6) {
let [isValid, message] = Validator_1.Validator.isValidIPv6String(expandedIPv6);
if (!isValid) {
throw new Error(message.filter(msg => { return msg !== ''; }).toString());
}
let stringHexadecimals = expandedIPv6.split(":");
let hexadecatet = stringHexadecimals.map((stringHexadecatet) => {
return Hexadecatet_1.Hexadecatet.fromString(stringHexadecatet);
});
let value = BigInt(`0b${(0, HexadecimalUtils_2.hexadectetNotationToBinaryString)(expandedIPv6)}`);
return [value, hexadecatet];
}
binaryStringToHexadecatets(binaryString) {
let hexadecimalString = (0, HexadecimalUtils_1.binaryStringToHexadecimalString)(binaryString);
while (hexadecimalString.length % 4 != 0) {
hexadecimalString = '0' + hexadecimalString;
}
let hexadecimalStrings = hexadecimalString.match(/.{1,4}/g);
return hexadecimalStrings.map((stringHexadecatet) => {
return Hexadecatet_1.Hexadecatet.fromString(stringHexadecatet);
});
}
}
exports.IPv6 = IPv6;
/**
* RFC 4193 private address range (fc00::/7 - Unique Local Addresses). This range is constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc4193
*/
IPv6.PRIVATE_RANGE = IPRange_2.IPv6CidrRange.fromCidr("fc00::/7");
/**
* RFC 3849 and RFC 9637 documentation address ranges. These ranges are constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc3849
* @see https://datatracker.ietf.org/doc/html/rfc9637
*/
IPv6.DOCUMENTATION_RANGES = [
IPRange_2.IPv6CidrRange.fromCidr("2001:db8::/32"),
IPRange_2.IPv6CidrRange.fromCidr("3fff::/20")
];
/**
* RFC 4291 multicast address range (ff00::/8). This range is constant and reused for performance.
*
* Multicast IPv6 address range:
* - ff00::/8 (ff00:: to ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff)
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
*/
IPv6.MULTICAST_RANGE = IPRange_2.IPv6CidrRange.fromCidr("ff00::/8");
/**
* RFC 4291 unspecified address range (::/128). This range is constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
*/
IPv6.UNSPECIFIED_RANGE = IPRange_2.IPv6CidrRange.fromCidr("::/128");
/**
* RFC 4291 loopback address range (::1/128). This range is constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
*/
IPv6.LOOPBACK_RANGE = IPRange_2.IPv6CidrRange.fromCidr("::1/128");
/**
* RFC 4291 link-local address range (fe80::/10). This range is constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc4291
*/
IPv6.LINK_LOCAL_RANGE = IPRange_2.IPv6CidrRange.fromCidr("fe80::/10");
/**
* RFC 6666 discard-only address range (100::/64). This range is constant and reused for performance.
*
* @see https://datatracker.ietf.org/doc/html/rfc6666
*/
IPv6.DISCARD_ONLY_RANGE = IPRange_2.IPv6CidrRange.fromCidr("100::/64");
/**
* The IPv4Mask can be seen as a specialized IPv4 number where, in a 32 bit number, starting from the left, you
* have continuous bits turned on (with 1 value) followed by bits turned off (with 0 value). In networking, it is used
* to demarcate which bits are used to identify a network, and the ones that are used to identify hosts on the network
*/
class IPv4Mask extends IPv4 {
/**
* A convenience method for creating an instance of IPv4Mask. The passed strings need to be a valid IPv4
* number in dot-decimal notation.
*
* @param {string} rawValue The passed string in dot-decimal notation
* @returns {IPv4Mask} the instance of IPv4Mask
*/
static fromDecimalDottedString(rawValue) {
return new IPv4Mask(rawValue);
}
;
/**
* Constructor for creating an instance of IPv4Mask.
* The passed strings need to be a valid IPv4 mask number in dot-decimal notation.
*
* @param {string} ipString The passed string in dot-decimal notation
*/
constructor(ipString) {
super(ipString);
/**
* An array of {@link Octet}'s
*
* @type {Array} the octets that makes up the IPv4Mask
*/
this.octets = [];
let isValid;
let message;
[isValid, message] = Validator_1.Validator.isValidIPv4Mask(ipString);
if (!isValid) {
throw new Error(message.filter(msg => { return msg !== ''; }).toString());
}
let stringOctets = ipString.split(".");
this.octets = stringOctets.map((rawOctet) => {
return Octet_1.Octet.fromString(rawOctet);
});
let binaryString = (0, BinaryUtils_1.dottedDecimalNotationToBinaryString)(ipString);
this.prefix = (binaryString.match(/1/g) || []).length;
this.value = BigInt(`0b${binaryString}`);
}
}
exports.IPv4Mask = IPv4Mask;
/**
* The IPv6Mask can be seen as a specialized IPv4 number where, in a 128 bit number, starting from the left,
* you have continuous bits turned on (with 1 value) followed by bits turned off (with 0 value). In networking, it
* is used to demarcate which bits are used to identify a network, and the ones that are used to identify hosts
* on the network
*/
class IPv6Mask extends IPv6 {
/**
* A convenience method for creating an instance of IPv6Mask.
* The passed strings need to be a valid IPv4 mask number in dot-decimal notation.
*
* @param {string} rawValue The passed string in textual notation
* @returns {IPv6Mask} the instance of IPv6Mask
*/
static fromHexadecatet(rawValue) {
return new IPv6Mask(rawValue);
}
;
/**
* Constructor for creating an instance of IPv6Mask.
* The passed strings need to be a valid IPv6 mask number in dot-decimal notation
*
* @param {string} ipString The passed IPv6 string
*/
constructor(ipString) {
super(ipString);
/**
* An array of {@link Hexadecatet}'s
*
* @type {Array} the hexadecatet that makes up the IPv6 number
*/
this.hexadecatet = [];
let isValid;
let message;
let expandedIPv6 = (0, IPv6Utils_1.expandIPv6Number)(ipString);
[isValid, message] = Validator_1.Validator.isValidIPv6Mask(expandedIPv6);
if (!isValid) {
throw new Error(message.filter(msg => { return msg !== ''; }).toString());
}
let stringHexadecimals = expandedIPv6.split(":");
this.hexadecatet = stringHexadecimals.map((stringHexadecatet) => {
return Hexadecatet_1.Hexadecatet.fromString(stringHexadecatet);
});
let binaryString = (0, HexadecimalUtils_2.hexadectetNotationToBinaryString)(expandedIPv6);
this.prefix = (binaryString.match(/1/g) || []).length;
this.value = BigInt(`0b${binaryString}`);
this.value = BigInt(`0b${(0, HexadecimalUtils_2.hexadectetNotationToBinaryString)(expandedIPv6)}`);
}
}
exports.IPv6Mask = IPv6Mask;
/**
* Check is the given IP number is an {@link IPv4} or not
* @param ip the IP number to check if it is IPv4.
*/
function isIPv4(ip) {
return ip.bitSize === 32;
}
//# sourceMappingURL=IPNumber.js.map