Merge branch 'klaxon1-feature/improve-parse-ip-range'

This commit is contained in:
n1474335 2018-08-29 18:57:08 +00:00
commit 9b9a182f9f
4 changed files with 266 additions and 8 deletions

View File

@ -3,6 +3,7 @@
*
* @author picapi
* @author n1474335 [n1474335@gmail.com]
* @author Klaxon [klaxon@veyr.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
@ -109,8 +110,8 @@ export function ipv6CidrRange(cidr, includeNetworkInfo) {
* @returns {string}
*/
export function ipv4HyphenatedRange(range, includeNetworkInfo, enumerateAddresses, allowLargeList) {
const ip1 = strToIpv4(range[1]),
ip2 = strToIpv4(range[2]);
const ip1 = strToIpv4(range[0].split("-")[0].trim()),
ip2 = strToIpv4(range[0].split("-")[1].trim());
let output = "";
@ -162,8 +163,8 @@ Total addresses in range: ${(((ip2 - ip1) >>> 0) + 1)}
* @returns {string}
*/
export function ipv6HyphenatedRange(range, includeNetworkInfo) {
const ip1 = strToIpv6(range[1]),
ip2 = strToIpv6(range[14]),
const ip1 = strToIpv6(range[0].split("-")[0].trim()),
ip2 = strToIpv6(range[0].split("-")[1].trim()),
total = new Array(128).fill();
let output = "",
@ -188,6 +189,93 @@ export function ipv6HyphenatedRange(range, includeNetworkInfo) {
return output;
}
/**
* Parses a list of IPv4 addresses separated by a new line (\n) and displays information
* about it.
*
* @param {RegExp} list
* @param {boolean} includeNetworkInfo
* @param {boolean} enumerateAddresses
* @param {boolean} allowLargeList
* @returns {string}
*/
export function ipv4ListedRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList) {
let ipv4List = match[0].split("\n");
ipv4List = ipv4List.filter(Boolean);
const ipv4CidrList = ipv4List.filter(function(a) {
return a.includes("/");
});
for (let i = 0; i < ipv4CidrList.length; i++) {
const network = strToIpv4(ipv4CidrList[i].split("/")[0]);
const cidrRange = parseInt(ipv4CidrList[i].split("/")[1], 10);
if (cidrRange < 0 || cidrRange > 31) {
return "IPv4 CIDR must be less than 32";
}
const mask = ~(0xFFFFFFFF >>> cidrRange),
cidrIp1 = network & mask,
cidrIp2 = cidrIp1 | ~mask;
ipv4List.splice(ipv4List.indexOf(ipv4CidrList[i]), 1);
ipv4List.push(ipv4ToStr(cidrIp1), ipv4ToStr(cidrIp2));
}
ipv4List = ipv4List.sort(ipv4Compare);
const ip1 = ipv4List[0];
const ip2 = ipv4List[ipv4List.length - 1];
const range = [ip1 + " - " + ip2];
return ipv4HyphenatedRange(range, includeNetworkInfo, enumerateAddresses, allowLargeList);
}
/**
* Parses a list of IPv6 addresses separated by a new line (\n) and displays information
* about it.
*
* @param {RegExp} list
* @param {boolean} includeNetworkInfo
* @returns {string}
*/
export function ipv6ListedRange(match, includeNetworkInfo) {
let ipv6List = match[0].split("\n");
ipv6List = ipv6List.filter(function(str) {
return str.trim();
});
for (let i =0; i < ipv6List.length; i++){
ipv6List[i] = ipv6List[i].trim();
}
const ipv6CidrList = ipv6List.filter(function(a) {
return a.includes("/");
});
for (let i = 0; i < ipv6CidrList.length; i++) {
const network = strToIpv6(ipv6CidrList[i].split("/")[0]);
const cidrRange = parseInt(ipv6CidrList[i].split("/")[1], 10);
if (cidrRange < 0 || cidrRange > 127) {
return "IPv6 CIDR must be less than 128";
}
const cidrIp1 = new Array(8),
cidrIp2 = new Array(8);
const mask = genIpv6Mask(cidrRange);
for (let j = 0; j < 8; j++) {
cidrIp1[j] = network[j] & mask[j];
cidrIp2[j] = cidrIp1[j] | (~mask[j] & 0x0000FFFF);
}
ipv6List.splice(ipv6List.indexOf(ipv6CidrList[i]), 1);
ipv6List.push(ipv6ToStr(cidrIp1), ipv6ToStr(cidrIp2));
}
ipv6List = ipv6List.sort(ipv6Compare);
const ip1 = ipv6List[0];
const ip2 = ipv6List[ipv6List.length - 1];
const range = [ip1 + " - " + ip2];
return ipv6HyphenatedRange(range, includeNetworkInfo);
}
/**
* Converts an IPv4 address from string format to numerical format.
*
@ -391,6 +479,37 @@ export function genIpv6Mask(cidr) {
return mask;
}
/**
* Comparison operation for sorting of IPv4 addresses.
*
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function ipv4Compare(a, b) {
return strToIpv4(a) - strToIpv4(b);
}
/**
* Comparison operation for sorting of IPv6 addresses.
*
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function ipv6Compare(a, b) {
const a_ = strToIpv6(a),
b_ = strToIpv6(b);
for (let i = 0; i < a_.length; i++){
if (a_[i] !== b_[i]){
return a_[i] - b_[i];
}
}
return 0;
}
const _LARGE_RANGE_ERROR = "The specified range contains more than 65,536 addresses. Running this query could crash your browser. If you want to run it, select the \"Allow large queries\" option. You are advised to turn off \"Auto Bake\" whilst editing large ranges.";
/**

View File

@ -1,12 +1,13 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @author Klaxon [klaxon@veyr.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import {ipv4CidrRange, ipv4HyphenatedRange, ipv6CidrRange, ipv6HyphenatedRange} from "../lib/IP";
import {ipv4CidrRange, ipv4HyphenatedRange, ipv4ListedRange, ipv6CidrRange, ipv6HyphenatedRange, ipv6ListedRange} from "../lib/IP";
/**
* Parse IP range operation
@ -21,7 +22,7 @@ class ParseIPRange extends Operation {
this.name = "Parse IP range";
this.module = "JSBN";
this.description = "Given a CIDR range (e.g. <code>10.0.0.0/24</code>) or a hyphenated range (e.g. <code>10.0.0.0 - 10.0.1.0</code>), this operation provides network information and enumerates all IP addresses in the range.<br><br>IPv6 is supported but will not be enumerated.";
this.description = "Given a CIDR range (e.g. <code>10.0.0.0/24</code>), hyphenated range (e.g. <code>10.0.0.0 - 10.0.1.0</code>), or a list of IPs and/or CIDR ranges (separated by a new line), this operation provides network information and enumerates all IP addresses in the range.<br><br>IPv6 is supported but will not be enumerated.";
this.infoURL = "https://wikipedia.org/wiki/Subnetwork";
this.inputType = "string";
this.outputType = "string";
@ -59,18 +60,24 @@ 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*$/,
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;
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,
ipv6ListRegex = /^\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?))?(\n|$)(\n*))+\s*$/i;
let match;
if ((match = ipv4CidrRegex.exec(input))) {
return ipv4CidrRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList);
} else if ((match = ipv4RangeRegex.exec(input))) {
return ipv4HyphenatedRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList);
} else if ((match = ipv4ListRegex.exec(input))) {
return ipv4ListedRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList);
} else if ((match = ipv6CidrRegex.exec(input))) {
return ipv6CidrRange(match, includeNetworkInfo);
} else if ((match = ipv6RangeRegex.exec(input))) {
return ipv6HyphenatedRange(match, includeNetworkInfo);
} else if ((match = ipv6ListRegex.exec(input))) {
return ipv6ListedRange(match, includeNetworkInfo);
} else {
throw new OperationError("Invalid input.\n\nEnter either a CIDR range (e.g. 10.0.0.0/24) or a hyphenated range (e.g. 10.0.0.0 - 10.0.1.0). IPv6 also supported.");
}

View File

@ -66,6 +66,7 @@ import "./tests/operations/TranslateDateTimeFormat";
import "./tests/operations/Magic";
import "./tests/operations/ToGeohash.mjs";
import "./tests/operations/FromGeohash.mjs";
import "./tests/operations/ParseIPRange";
let allTestsPassing = true;
const testStatusCounts = {
@ -144,4 +145,3 @@ TestRegister.runTests()
process.exit(allTestsPassing ? 0 : 1);
});

View File

@ -0,0 +1,132 @@
/**
* Parse IP Range tests.
*
* @author Klaxon [klaxon@veyr.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister";
TestRegister.addTests([
{
name: "Parse IPv4 CIDR",
input: "10.0.0.0/30",
expectedOutput: "Network: 10.0.0.0\nCIDR: 30\nMask: 255.255.255.252\nRange: 10.0.0.0 - 10.0.0.3\nTotal addresses in range: 4\n\n10.0.0.0\n10.0.0.1\n10.0.0.2\n10.0.0.3",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "Parse IPv4 hyphenated",
input: "10.0.0.0 - 10.0.0.3",
expectedOutput: "Minimum subnet required to hold this range:\n\tNetwork: 10.0.0.0\n\tCIDR: 30\n\tMask: 255.255.255.252\n\tSubnet range: 10.0.0.0 - 10.0.0.3\n\tTotal addresses in subnet: 4\n\nRange: 10.0.0.0 - 10.0.0.3\nTotal addresses in range: 4\n\n10.0.0.0\n10.0.0.1\n10.0.0.2\n10.0.0.3",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "Parse IPv4 list",
input: "10.0.0.8\n10.0.0.5/30\n10.0.0.1\n10.0.0.3",
expectedOutput: "Minimum subnet required to hold this range:\n\tNetwork: 10.0.0.0\n\tCIDR: 28\n\tMask: 255.255.255.240\n\tSubnet range: 10.0.0.0 - 10.0.0.15\n\tTotal addresses in subnet: 16\n\nRange: 10.0.0.1 - 10.0.0.8\nTotal addresses in range: 8\n\n10.0.0.1\n10.0.0.2\n10.0.0.3\n10.0.0.4\n10.0.0.5\n10.0.0.6\n10.0.0.7\n10.0.0.8",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "Parse IPv6 CIDR - full",
input: "2404:6800:4001:0000:0000:0000:0000:0000/48",
expectedOutput: "Network: 2404:6800:4001:0000:0000:0000:0000:0000\nShorthand: 2404:6800:4001::\nCIDR: 48\nMask: ffff:ffff:ffff:0000:0000:0000:0000:0000\nRange: 2404:6800:4001:0000:0000:0000:0000:0000 - 2404:6800:4001:ffff:ffff:ffff:ffff:ffff\nTotal addresses in range: 1.2089258196146292e+24\n\n",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "Parse IPv6 CIDR - collapsed",
input: "2404:6800:4001::/48",
expectedOutput: "Network: 2404:6800:4001:0000:0000:0000:0000:0000\nShorthand: 2404:6800:4001::\nCIDR: 48\nMask: ffff:ffff:ffff:0000:0000:0000:0000:0000\nRange: 2404:6800:4001:0000:0000:0000:0000:0000 - 2404:6800:4001:ffff:ffff:ffff:ffff:ffff\nTotal addresses in range: 1.2089258196146292e+24\n\n",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "Parse IPv6 hyphenated",
input: "2404:6800:4001:: - 2404:6800:4001:ffff:ffff:ffff:ffff:ffff",
expectedOutput: "Range: 2404:6800:4001:0000:0000:0000:0000:0000 - 2404:6800:4001:ffff:ffff:ffff:ffff:ffff\nShorthand range: 2404:6800:4001:: - 2404:6800:4001:ffff:ffff:ffff:ffff:ffff\nTotal addresses in range: 1.2089258196146292e+24\n\n",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "Parse IPv6 list",
input: "2404:6800:4001:ffff:ffff:ffff:ffff:ffff\n2404:6800:4001::ffff\n2404:6800:4001:ffff:ffff::1111\n2404:6800:4001::/64",
expectedOutput: "Range: 2404:6800:4001:0000:0000:0000:0000:0000 - 2404:6800:4001:ffff:ffff:ffff:ffff:ffff\nShorthand range: 2404:6800:4001:: - 2404:6800:4001:ffff:ffff:ffff:ffff:ffff\nTotal addresses in range: 1.2089258196146292e+24\n\n",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "IPv4 subnet out of range error",
input: "10.1.1.1/34",
expectedOutput: "IPv4 CIDR must be less than 32",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "invalid IPv4 address error",
input: "444.1.1.1/30",
expectedOutput: "Block out of range.",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "IPv6 subnet out of range error",
input: "2404:6800:4001::/129",
expectedOutput: "IPv6 CIDR must be less than 128",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
{
name: "invalid IPv6 address error",
input: "2404:6800:4001:/12",
expectedOutput: "Invalid input.\n\nEnter either a CIDR range (e.g. 10.0.0.0/24) or a hyphenated range (e.g. 10.0.0.0 - 10.0.1.0). IPv6 also supported.",
recipeConfig: [
{
"op": "Parse IP range",
"args": [true, true, false]
},
],
},
]);