@eryldor/cidr
Version:
A javascript library to manipulate CIDR blocks
147 lines • 5.98 kB
JavaScript
;
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
Object.defineProperty(exports, "__esModule", { value: true });
class CIDRBlock {
constructor(networkAddr, prefix) {
this.innerNetworkAddr = networkAddr;
this.networkPrefix = prefix;
}
/**
* Construct a [[CIDRBlock]] object from four IP component numbers (eg: a.b.c.d) and a prefix. The network
* address is sanitized by zeroing all the bits after the prefix.
* @throws If any IP component number is invalid or if the prefix is invalid.
*/
static fromNumbers(a, b, c, d, prefix) {
const { addr, prefix: netPrefix } = CIDRBlock.fromNumbersInner(a, b, c, d, prefix);
return new CIDRBlock(addr, netPrefix);
}
/**
* Parse a string into a CIDR block.
* @throws If the stirng is not a valid CIDR block
*/
static fromString(cidr) {
const { addr, prefix } = CIDRBlock.fromStringInner(cidr);
return new CIDRBlock(addr, prefix);
}
static fromNumbersInner(a, b, c, d, prefix) {
if (![a, b, c, d].every((n) => CIDRBlock.checkIPBoundaries(n))) {
throw new Error(`Invalid network address for ${a}.${b}.${c}.${d}/${prefix}`);
}
if (!CIDRBlock.checkPrefix(prefix)) {
throw new Error(`Invalid prefix address for ${a}.${b}.${c}.${d}/${prefix}`);
}
let addr = ((a << 24) | (b << 16) | (c << 8) | d) >>> 0;
// Sanitize the network address
const sanitizer = (0xFFFFFFFF << (32 - prefix) >>> 0);
addr = (addr & sanitizer) >>> 0;
return {
addr,
prefix,
};
}
static fromStringInner(cidr) {
const splittedCidr = cidr.split("/");
if (splittedCidr.length !== 2) {
throw new Error(`"${cidr} is not a valid CIDR`);
}
const prefix = Number.parseInt(splittedCidr[1], 10);
if (prefix === null || !CIDRBlock.checkPrefix(prefix)) {
throw new Error(`"${prefix}" is not a valid prefix in CIDR "${cidr}"`);
}
const splittedNetworkAddr = splittedCidr[0].split(".")
.map((i) => Number.parseInt(i, 10))
.filter((n) => !Number.isNaN(n));
if (splittedNetworkAddr.length !== 4) {
throw new Error(`"${splittedCidr[0]}" is not a valid network address in CIDR "${cidr}"`);
}
return CIDRBlock.fromNumbersInner(splittedNetworkAddr[0], splittedNetworkAddr[1], splittedNetworkAddr[2], splittedNetworkAddr[3], prefix);
}
static checkIPBoundaries(n) {
return Number.isInteger(n) && n >= 0 && n <= 255;
}
static checkPrefix(prefix) {
return Number.isInteger(prefix) && prefix >= 0 && prefix <= 32;
}
static networkAddressToString(netAddr) {
const a = netAddr >> 24;
const b = netAddr >> 16 & 0xFF;
const c = netAddr >> 8 & 0xFF;
const d = netAddr & 0xFF;
const networkAddr = new Uint8Array([a, b, c, d]);
return `${networkAddr[0]}.${networkAddr[1]}.${networkAddr[2]}.${networkAddr[3]}`;
}
get networkAddress() {
return CIDRBlock.networkAddressToString(this.innerNetworkAddr);
}
/**
* Return the broadcast address of this CIDR block.
*/
get broadcastAddress() {
const broadcastAddress = this.innerNetworkAddr | (0xFFFFFFFF >>> this.networkPrefix);
return CIDRBlock.networkAddressToString(broadcastAddress);
}
/**
* Return a generator yielding all the IP address this block can contain. It doesn't yield the network address and
* the broadcast address.
*/
*ipAddress() {
for (let i = 1; i < this.maxAddressIndex; i++) {
yield this.getAddress(i);
}
}
/**
* Return the CIDR notation of this block
*/
toString() {
return `${this.networkAddress}/${this.networkPrefix}`;
}
/**
* Split the CIDR block into at least `requiredSubnetAmount`.
* @returns The new CIDR blocks generated from the split
* @throws If the block can't be divided by at least `requiredSubnetAmount` or if `requiredSubnetAmount` is negative
*/
split(requiredSubnetAmount) {
if (requiredSubnetAmount <= 0) {
throw new Error("The required subnet amount must be positive");
}
const prefixDiff = Math.ceil(Math.log2(requiredSubnetAmount));
const newPrefix = this.networkPrefix + prefixDiff;
if (!CIDRBlock.checkPrefix(newPrefix)) {
throw new Error(`Can't divide "${this.toString()}" into ${requiredSubnetAmount}`);
}
const resultLength = Math.pow(2, prefixDiff);
const result = [];
for (let i = 0; i < resultLength; i++) {
const networkAddr = this.innerNetworkAddr + (i << (32 - newPrefix));
result.push(new CIDRBlock(networkAddr, newPrefix));
}
return result;
}
/**
* Return the maximum address index usable with {@link CIDRBlock.getAddress}
*/
get maxAddressIndex() {
return 0xFFFFFFFF >>> this.networkPrefix;
}
/**
* Return the i-th address inside this CIDR Block.
*
* Note that `getAddress(0)` is equivalent [[CIDRBlock.networkAddress]],
* `this.getAddress(this.maxAddressIndex)` is equivaent to [[CIDRBlock.broadcastAddress]]
*
* @throws if `i` is not an integer between 0 and [[CIDRBlock.maxAddressIndex]]
*/
getAddress(i) {
if (!Number.isInteger(i) || i < 0 || i > this.maxAddressIndex) {
throw new Error(`${i} is not a valid address index for "${this.toString()}"`);
}
const addr = this.innerNetworkAddr + i;
return CIDRBlock.networkAddressToString(addr);
}
}
exports.CIDRBlock = CIDRBlock;
//# sourceMappingURL=cidrBlock.js.map