@corvina/cidr
Version:
CIDR Operation helper
349 lines (344 loc) • 10.4 kB
JavaScript
const invalidChars = /^.*?(?=[\^#%&$\*:<>\?\/\{\|\}\\\s[a-zA-Z]).*$/;
var ErrorMessages;
(function (ErrorMessages) {
ErrorMessages["INVALID_IP_ILLEGAL_CHARACTER"] = "INVALID_IP_ILLEGAL_CHARACTER";
ErrorMessages["INVALID_ADDRESS_NOT_ENOUGH_QUADS"] = "INVALID_ADDRESS_NOT_ENOUGH_QUADS";
ErrorMessages["INVALID_IP_INVALID_QUAD"] = "INVALID_IP_INVALID_QUAD";
ErrorMessages["INVALID_IP_QUAD_TOO_LARGE"] = "INVALID_IP_QUAD_TOO_LARGE";
ErrorMessages["INVALID_IP_QUAD_TOO_SMALL"] = "INVALID_IP_QUAD_TOO_SMALL";
ErrorMessages["INVALID_BITMASK_VALUE"] = "INVALID_BITMASK_VALUE";
ErrorMessages["INVALID_CIDR_BETTER_EXPRESSION"] = "INVALID_CIDR_BETTER_EXPRESSION";
})(ErrorMessages || (ErrorMessages = {}));
const intCommonCidr = (ips) => {
const ipInt = ips.sort();
let mask = 0;
const range = ipInt[ipInt.length - 1] - ipInt[0];
let baseIp = ipInt[0];
for (let i = 0; i <= 32; i++) {
mask = 32 - i;
const exp = Math.pow(2, (32 - mask));
if (exp - 1 >= range) {
if (ipInt[0] % exp != 0) {
baseIp = ipInt[0] - (ipInt[0] % exp);
}
if (ipInt[ipInt.length - 1] > baseIp + exp) {
mask--;
}
break;
}
}
return `${toString(baseIp)}/${mask}`;
};
const padLeft = (input, char, min) => {
while (input.length < min) {
input = char + input;
}
return input;
};
const toInt = (ipAddress) => ipAddress
.split(".")
.reduce((p, c, i) => p + parseInt(c) * Math.pow(256, (3 - i)), 0);
const toString = (ipInt) => {
let remaining = ipInt;
let address = [];
for (let i = 0; i < 4; i++) {
if (remaining != 0) {
address.push(Math.floor(remaining / Math.pow(256, (3 - i))));
remaining = remaining % Math.pow(256, (3 - i));
}
else {
address.push(0);
}
}
return address.join(".");
};
const ipCommonCidr = (ips) => {
const ipInt = ips.map(toInt);
return intCommonCidr(ipInt);
};
const toOctets = (input) => {
if (typeof input === "number") {
input = toString(input);
}
return input.split(".").map((x) => parseInt(x));
};
const reverse = (ip) => {
if (typeof ip === "number") {
ip = toString(ip);
}
const octets = toOctets(ip);
return `${octets[3]}.${octets[2]}.${octets[1]}.${octets[0]}.in-addr.arpa`;
};
const toBinary = (ip) => {
const octets = toOctets(ip);
let o = [];
for (let i = 0; i < 4; i++) {
o[i] = padLeft((octets[i] >>> 0).toString(2), "0", 8);
}
return `${o[0]}.${o[1]}.${o[2]}.${o[3]}`;
};
const binaryToIpString = (binary) => {
const octets = binary.split('.').map((b) => parseInt(b, 2));
return octets.join('.');
};
const toHex = (ip) => {
if (typeof ip === "string") {
ip = toInt(ip);
}
return ip.toString(16);
};
const next = (ip) => toString(toInt(ip) + 1);
const previous = (ip) => toString(toInt(ip) - 1);
const toCidr = (ip) => {
if (typeof ip === "number") {
ip = toString(ip);
}
return `${ip}/32`;
};
const validateIp = (ip) => {
if (invalidChars.test(ip))
return ErrorMessages.INVALID_IP_ILLEGAL_CHARACTER;
const octets = ip.split(".");
if (octets.length !== 4)
return ErrorMessages.INVALID_ADDRESS_NOT_ENOUGH_QUADS;
for (let i = 0; i < octets.length; i++) {
const int = parseInt(octets[i]);
if (isNaN(int))
return ErrorMessages.INVALID_IP_INVALID_QUAD;
if (int > 255)
return ErrorMessages.INVALID_IP_QUAD_TOO_LARGE;
if (int < 0)
return ErrorMessages.INVALID_IP_QUAD_TOO_SMALL;
}
return null;
};
const ip = {
toInt,
toString,
commonCidr: ipCommonCidr,
toHex,
toOctets,
toBinary,
reverse,
previous,
next,
toCidr,
validate: validateIp,
binaryToIpString
};
const address = (ip) => ip.split("/")[0];
const maskString = (ip) => ip.split("/")[1];
const mask = (ip) => parseInt(maskString(ip));
const toIntRange = (cidr) => [
toInt(min(cidr)),
toInt(max(cidr)),
];
const toRange = (cidr) => [min(cidr), max(cidr)];
const cidrCommonCidr = (cidrs) => {
const ipMap = cidrs.map((x) => toIntRange(x));
const ipInt = [].concat.apply([], ipMap).sort();
return intCommonCidr(ipInt);
};
const netmask = (cidr) => toString(Math.pow(2, 32) - Math.pow(2, (32 - mask(cidr))));
const broadcast = (cidr) => max(cidr);
const min = (cidr) => {
const addr = address(cidr);
const addrInt = toInt(addr);
const div = addrInt % Math.pow(2, (32 - mask(cidr)));
return div > 0 ? toString(addrInt - div) : addr;
};
const max = (cidr) => {
let initial = toInt(min(cidr));
let add = Math.pow(2, (32 - mask(cidr)));
return toString(initial + add - 1);
};
const count = (cidr) => Math.pow(2, (32 - mask(cidr)));
const usable = (cidr, options) => {
const { startStopOnly = false } = options || {};
let mask = maskString(cidr);
if (!mask) {
return [];
}
if (mask === '32')
return [address(cidr)];
if (mask === '31')
return ips(cidr);
const result = [];
let start = toInt(min(cidr)) + 1;
const stop = toInt(max(cidr));
if (startStopOnly) {
return [toString(start), toString(stop - 1)];
}
while (start < stop) {
result.push(toString(start));
start += 1;
}
return result;
};
const wildcardmask = (cidr) => toString(Math.pow(2, (32 - mask(cidr))) - 1);
const subnets = (cidr, subMask, limit) => {
mask(cidr);
let count = toInt(address(cidr));
let maxIp = toInt(max(cidr));
let subnets = [];
let step = Math.pow(2, (32 - subMask));
if (limit) {
limit = count + limit * step;
if (limit < maxIp) {
maxIp = limit;
}
}
while (count < maxIp) {
subnets.push(`${toString(count)}/${subMask}`);
count += step;
}
return subnets;
};
const ips = (cidr) => {
let ips = [];
const maxIp = toInt(max(cidr));
let current = address(cidr);
while (toInt(current) <= maxIp) {
ips.push(current);
current = next(current);
}
return ips;
};
const includes = (cidr, ip) => {
const ipInt = toInt(ip);
return ipInt >= toInt(min(cidr)) && ipInt <= toInt(max(cidr));
};
const nextCidr = (cidr) => `${toString(toInt(address(cidr)) + Math.pow(2, (32 - mask(cidr))))}/${mask(cidr)}`;
const previousCidr = (cidr) => `${toString(toInt(address(cidr)) - Math.pow(2, (32 - mask(cidr))))}/${mask(cidr)}`;
const nthCidr = (cidr, nth) => `${toString(toInt(address(cidr)) + Math.pow(2, (32 - mask(cidr))) * nth)}/${mask(cidr)}`;
const random = (cidr) => {
const [minIp, maxIp] = toIntRange(cidr);
return toString(Math.floor(Math.random() * (maxIp - minIp + 1)) + minIp);
};
const VALID_BITMASKS = [
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', '10', '11',
'12', '13', '14', '15', '16', '17',
'18', '19', '20', '21', '22', '23',
'24', '25', '26', '27', '28', '29',
'30', '31', '32'
];
const validateCidr = (cidr) => {
const ip = address(cidr);
const ipValid = validateIp(ip);
if (ipValid !== null)
return ipValid;
const cidrMask = maskString(cidr);
if (!VALID_BITMASKS.includes(cidrMask))
return ErrorMessages.INVALID_BITMASK_VALUE;
if (ip !== min(cidr))
return ErrorMessages.INVALID_CIDR_BETTER_EXPRESSION;
return null;
};
const cidr = {
toRange,
usable,
toIntRange,
commonCidr: cidrCommonCidr,
max,
min,
count,
netmask,
wildcardmask,
broadcast,
subnets,
ips,
includes,
random,
next: nextCidr,
previous: previousCidr,
nth: nthCidr,
address,
mask,
validate: validateCidr,
};
var cidr$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
get ErrorMessages () { return ErrorMessages; },
ip: ip,
cidr: cidr
});
function validateIpAddressForEndpoint(value) {
if (!value) {
throw new Error('Invalid');
}
let [address, mask] = value.split('/');
if (!mask) {
const err = ip.validate(address);
if (err) {
throw new Error(err);
}
return value;
}
else {
if (mask.includes('.')) {
const err = ip.validate(mask);
if (err) {
throw new Error(err);
}
mask = convertNetmaskToBitmask(mask).toString();
value = `${address}/${mask}`;
}
value = `${cidr.min(value)}/${mask}`;
const err = cidr.validate(value);
if (err) {
throw new Error(err);
}
return value;
}
}
function convertNetworkAddressWithBitmaskToNetmask(value) {
if (value.includes('/')) {
let [address, mask] = value.split('/');
if (!mask) {
return value;
}
if (!mask.includes('.')) {
mask = convertBitmaskToNetmask(parseInt(mask)).toString();
value = `${address}/${mask}`;
return value;
}
}
return value;
}
function convertBitmaskToNetmask(bitCount) {
var mask = [], i, n;
for (i = 0; i < 4; i++) {
n = Math.min(bitCount, 8);
mask.push(256 - Math.pow(2, 8 - n));
bitCount -= n;
}
return mask.join('.');
}
function convertNetmaskToBitmask(netmask) {
const netmaskOctets = netmask.split('.');
const netmaskBits = netmaskOctets.map((octet) => parseInt(octet, 10).toString(2));
const bitmask = netmaskBits
.join('')
.split('')
.filter((bit) => bit === '1').length;
return bitmask;
}
function anonymizeIp(ipAddress, maskLength) {
const binaryAddress = ip.toBinary(ipAddress);
const anonymizedBinaryAddress = binaryAddress
.replace(/\./g, '')
.slice(0, maskLength)
.padEnd(32, '0')
.replace(/(.{8})(?=.)/g, '$1.');
return ip.binaryToIpString(anonymizedBinaryAddress);
}
var endpoint = /*#__PURE__*/Object.freeze({
__proto__: null,
validateIpAddressForEndpoint: validateIpAddressForEndpoint,
convertNetworkAddressWithBitmaskToNetmask: convertNetworkAddressWithBitmaskToNetmask,
convertBitmaskToNetmask: convertBitmaskToNetmask,
convertNetmaskToBitmask: convertNetmaskToBitmask,
anonymizeIp: anonymizeIp
});
export { cidr$1 as cidr, endpoint };