UNPKG

iobroker.net-tools

Version:

This adapter cyclic polls configured IPs, can send wake-on-lan packages and scan for open ports.

297 lines (258 loc) 9.04 kB
const IP_DECIMAL_BASE = 256; const IP_LOOP_START = 3; const IP_LOOP_END = 0; function calculate(ipStart, ipEnd) { let ipStartNum, ipEndNum, ipCurNum; let rangeCollection = []; try { ipStartNum = convertIpToDecimal(ipStart); ipEndNum = convertIpToDecimal(ipEnd); } catch(err) { return null; } if(ipEndNum < ipStartNum) { return null; } ipCurNum = ipStartNum; while(ipCurNum <= ipEndNum) { let optimalRange = this.getOptimalRange(ipCurNum, ipEndNum); if(optimalRange === null) { return null; } rangeCollection.push(optimalRange); ipCurNum = optimalRange.ipHigh + 1; } return rangeCollection; } /** * Calculates a subnet mask from CIDR prefix. * * @param {string|number} ip IP address ("2.3.4.5") * @param {number} prefixSize Number of relevant bits in the subnet mask (24) * @return {object|null} Returns null in case of an error, and a subnet data object otherwise. * For details about the subnet data object, see documentation of * getMaskRange() * @public */ function calculateSubnetMask(ip, prefixSize) { // Convert the IP address to decimal let ipAddressInDecimal; try { ipAddressInDecimal = convertIpToDecimal(ip); } catch(err) { return null; } // Use the decimal IP address to calculate the subnet mask return getMaskRange(ipAddressInDecimal, prefixSize); } /** * Calculates a CIDR prefix from subnet mask. * * @param {string | number} ip - The IP address (Ex: "2.3.4.5") * @param {string | number} subnetMask - The subnet mask for the IP (Ex: "255.255.255.0") * @returns {Object | null} - It returns `null` in case of an error, and an object containing the subnet data if successful. * @public */ function calculateCIDRPrefix (ip, subnetMask) { const IPv4_BIT_COUNT = 32; let ipDecimal, subnetMaskDecimal, currentPrefix = 0, updatedPrefix = 0, subnetBitCount; function calculateNewPrefix(i) { updatedPrefix = (currentPrefix + (1 << (IPv4_BIT_COUNT - (i + 1))) >>> 0); if ((subnetMaskDecimal & updatedPrefix) >>> 0 !== updatedPrefix) { return i; } currentPrefix = updatedPrefix; } try { ipDecimal = convertIpToDecimal(ip); subnetMaskDecimal = convertIpToDecimal(subnetMask); } catch (err) { return null; } for (subnetBitCount = 0; subnetBitCount < IPv4_BIT_COUNT; subnetBitCount++) { let returnValue = calculateNewPrefix(subnetBitCount); if (returnValue !== undefined) { subnetBitCount = returnValue; break; } } return getMaskRange(ipDecimal, subnetBitCount); } function getOptimalRange(ipNum, ipEndNum) { let prefixSize, optimalRange = null; for(prefixSize = 32; prefixSize >= 0; prefixSize--) { let maskRange = getMaskRange(ipNum, prefixSize); if((maskRange.ipLow === ipNum) && (maskRange.ipHigh <= ipEndNum)) { optimalRange = maskRange; } else { break; } } return optimalRange; } function getMaskRange(ipNum, prefixSize) { const prefixMask = getPrefixMask(prefixSize); const lowMask = getMask(32 - prefixSize); const ipLow = calculateIpLow(ipNum, prefixMask); const ipHigh = calculateIpHigh(ipNum, prefixMask, lowMask); const invertedSize = 32 - prefixSize; return buildMaskRangeObject(ipLow, ipHigh, prefixMask, invertedSize, lowMask, prefixSize); } function calculateIpLow(ipNum, prefixMask) { return (ipNum & prefixMask) >>> 0; } function calculateIpHigh(ipNum, prefixMask, lowMask) { return ((ipNum & prefixMask) >>> 0) + lowMask >>> 0; } function buildMaskRangeObject(ipLow, ipHigh, prefixMask, invertedSize, lowMask, prefixSize) { return { ipLow: ipLow, ipLowStr: toString(ipLow), ipHigh: ipHigh, ipHighStr: toString(ipHigh), prefixMask: prefixMask, prefixMaskStr: toString(prefixMask), prefixSize: prefixSize, invertedMask: lowMask, invertedMaskStr: toString(lowMask), invertedSize: invertedSize }; } function getPrefixMask ( prefixSize ) { let mask = 0; for(let i = 0; i < prefixSize; i++ ) { let maskBit = ( 1 << ( 32 - ( i + 1 ) ) ) >>> 0; mask += maskBit; } return mask; } /** * Returns a bitmask with maskSize rightmost bits set to one * * @param {Number} maskSize Number of bits to be set * @return {Number} Returns the bitmask * @private */ function getMask(maskSize) { let bitMask = 0; const BASE = 1; const DEFAULT_VALUE = 0; const raisePower = function(bitIndex) { return (BASE << bitIndex) >>> DEFAULT_VALUE; } for(let bitIndex = 0; bitIndex < maskSize; bitIndex++) { bitMask += raisePower(bitIndex); } return bitMask; } /** * Check if a part of an IP address is valid * @param {string} part * @returns {boolean} * @private */ function isValidIpPart(part) { let n = parseInt(part, 10); return n >= 0 && n <= 255; } /** * Test whether string is an IP address * @param {string} ip * @returns {boolean} * @public */ function isIp(ip) { if (typeof ip !== 'string') { return false; } let parts = ip.match(/^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/); if (parts === null) { return false; } for (let i = 1; i <= 4; i++) { if (!isValidIpPart(parts[i])) { return false; } } return true; } /** * Test whether number is an IP address * @param {number} ipNum * @returns {boolean} * @public */ function isDecimalIp( ipNum ) { return ( ( typeof ipNum === 'number' ) && // is this a number? ( ipNum % 1 === 0 ) && // does the number have a decimal place? ( ipNum >= 0 ) && ( ipNum <= 4294967295 ) ); } /** * Converts string formatted IPs to decimal representation * * @param {string|number} ipString IP address in string format. If a decimal representation given, it is returned unmodified. * @return {number} Returns the IP address in decimal format * @throws {Error} Throws an error, if `ipString` does not contain an IP address. * @private */ function convertIpToDecimal(ipString) { const BASE = 256; if(typeof ipString === 'number' && isDecimalIp(ipString)) { return ipString; } if(!isIp(ipString)) { throw new Error('Not an IP address: ' + ipString); } const ipParts = ipString.split('.').map(part => +part); return calculateIpFromParts(ipParts, BASE); } /** * Calculate the decimal representation of IP from parts * * @param {Array} ipParts * @param {number} base * @return {number} * @private */ function calculateIpFromParts(ipParts, base) { return ((((ipParts[0] * base + ipParts[1]) * base) + ipParts[2]) * base) + ipParts[3]; } /** * Converts decimal IPs to string representation * * @link http://javascript.about.com/library/blipconvert.htm * @param {number} ipNum IP address in decimal format. If a string representation is given, it is returned unmodified. * @return {string} Returns the IP address in string format * @throws {Error} Throws an error if `ipNum` is out of range, not a decimal, or not a number * @private */ function calculateIpAddress(ipNum) { let d = ipNum % IP_DECIMAL_BASE; ipNum = Math.floor(ipNum / IP_DECIMAL_BASE); for (let i = IP_LOOP_START; i > IP_LOOP_END; i--) { d = ipNum % IP_DECIMAL_BASE + '.' + d; ipNum = Math.floor(ipNum / IP_DECIMAL_BASE); } return d; } function toString(ipNum) { if ((typeof ipNum === 'string') && (isIp(ipNum) === true)) { return ipNum; } if (isDecimalIp(ipNum) === false) { throw new Error('Not a numeric IP address: ' + ipNum); } let d = ipNum % IP_DECIMAL_BASE; return calculateIpAddress(ipNum); } module.exports = { calculateSubnetMask: calculateSubnetMask }