diff --git a/src/core/lib/IP.mjs b/src/core/lib/IP.mjs index c97f87ab..41bada93 100644 --- a/src/core/lib/IP.mjs +++ b/src/core/lib/IP.mjs @@ -189,6 +189,55 @@ export function ipv6HyphenatedRange(range, includeNetworkInfo) { return output; } +/** + * Parses an IPv4 subnet mask (e.g. 192.168.0.0/255.255.255.0) and displays information about it. + * + * @param {RegExp} subnetMask + * @param {boolean} includeNetworkInfo + * @param {boolean} enumerateAddresses + * @param {boolean} allowLargeList + * @returns {string} + */ +export function ipv4SubnetMask(subnetMask, includeNetworkInfo, enumerateAddresses, allowLargeList) { + const network = strToIpv4(subnetMask[1]), + mask = subnetMask[2]; + let output = ""; + + // Calculate the CIDR + const octets = mask.split(".").map(Number); + let cidr = 0; + for (let i = 0; i < octets.length; i++) { + let octet = octets[i]; + while (octet) { + cidr += (octet & 1); // Check if the last bit is 1 + octet >>= 1; // Shift bits to the right + } + } + if (cidr < 0 || cidr > 31) { + throw new OperationError("IPv4 CIDR must be less than 32"); + } + + const ip1 = network & strToIpv4(mask), + ip2 = ip1 | ~strToIpv4(mask); + + if (includeNetworkInfo) { + output += "Network: " + ipv4ToStr(network) + "\n"; + output += "CIDR: " + cidr + "\n"; + output += "Mask: " + mask + "\n"; + output += "Range: " + ipv4ToStr(ip1) + " - " + ipv4ToStr(ip2) + "\n"; + output += "Total addresses in range: " + (((ip2 - ip1) >>> 0) + 1) + "\n\n"; + } + + if (enumerateAddresses) { + if (cidr >= 16 || allowLargeList) { + output += generateIpv4Range(ip1, ip2).join("\n"); + } else { + output += _LARGE_RANGE_ERROR; + } + } + return output; +} + /** * Parses a list of IPv4 addresses separated by a new line (\n) and displays information * about it. diff --git a/src/core/operations/ParseIPRange.mjs b/src/core/operations/ParseIPRange.mjs index 2c59c015..5a2496e3 100644 --- a/src/core/operations/ParseIPRange.mjs +++ b/src/core/operations/ParseIPRange.mjs @@ -7,7 +7,7 @@ import Operation from "../Operation.mjs"; import OperationError from "../errors/OperationError.mjs"; -import {ipv4CidrRange, ipv4HyphenatedRange, ipv4ListedRange, ipv6CidrRange, ipv6HyphenatedRange, ipv6ListedRange} from "../lib/IP.mjs"; +import {ipv4CidrRange, ipv4HyphenatedRange, ipv4SubnetMask, ipv4ListedRange, ipv6CidrRange, ipv6HyphenatedRange, ipv6ListedRange} from "../lib/IP.mjs"; /** * Parse IP range operation @@ -60,6 +60,7 @@ class ParseIPRange extends Operation { // Check what type of input we are looking at const ipv4CidrRegex = /^\s*((?:\d{1,3}\.){3}\d{1,3})\/(\d\d?)\s*$/, ipv4RangeRegex = /^\s*((?:\d{1,3}\.){3}\d{1,3})\s*-\s*((?:\d{1,3}\.){3}\d{1,3})\s*$/, + ipv4SubnetMaskRegex = /^\s*((?:\d{1,3}\.){3}\d{1,3})\/((?:\d{1,3}\.){3}\d{1,3})\s*$/, ipv4ListRegex = /^\s*(((?:\d{1,3}\.){3}\d{1,3})(\/(\d\d?))?(\n|$)(\n*))+\s*$/, ipv6CidrRegex = /^\s*(((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\4)::|:\b|(?![\dA-F])))|(?!\3\4)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\/(\d\d?\d?)\s*$/i, ipv6RangeRegex = /^\s*(((?=.*::)(?!.*::[^-]+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\4)::|:\b|(?![\dA-F])))|(?!\3\4)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\s*-\s*(((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\17)::|:\b|(?![\dA-F])))|(?!\16\17)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\s*$/i, @@ -70,6 +71,8 @@ class ParseIPRange extends Operation { return ipv4CidrRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList); } else if ((match = ipv4RangeRegex.exec(input))) { return ipv4HyphenatedRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList); + } else if ((match = ipv4SubnetMaskRegex.exec(input))) { + return ipv4SubnetMask(match, includeNetworkInfo, enumerateAddresses, allowLargeList); } else if ((match = ipv4ListRegex.exec(input))) { return ipv4ListedRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList); } else if ((match = ipv6CidrRegex.exec(input))) {