UNPKG

@corvina/cidr

Version:

CIDR Operation helper

349 lines (344 loc) 10.4 kB
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 };