mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 22:07:10 -04:00
feat(new tool): IPv6 Subnet Calculator
IPv6 Subnet Calculator Fix #354 and #924
This commit is contained in:
parent
d7a8c65067
commit
1f67ba15e9
10 changed files with 285 additions and 111 deletions
|
@ -13,12 +13,16 @@ const IPv6MAX = (BigInt(2) ** BigInt(128)) - BigInt(1);
|
|||
*/
|
||||
|
||||
export default class IP {
|
||||
integer: bigint;
|
||||
short: string;
|
||||
version: number;
|
||||
address: string;
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
constructor(address) {
|
||||
this.integer = 0;
|
||||
this.short = 0;
|
||||
constructor(address: string) {
|
||||
this.integer = 0n;
|
||||
this.short = '';
|
||||
this.version = this._checkVersion(address);
|
||||
this.address = this._checkAddress(address, this.version);
|
||||
}
|
||||
|
@ -34,7 +38,7 @@ export default class IP {
|
|||
if (this.version === 4) {
|
||||
const splittedAddr = this.address.split('.').reverse();
|
||||
bigInt = splittedAddr.reduce((bigInt, octet, index) => {
|
||||
return (octet * 256 ** index + bigInt
|
||||
return (Number(octet) * 256 ** index + bigInt
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
|
@ -51,7 +55,7 @@ export default class IP {
|
|||
* @param {bigint} bigInt
|
||||
* @return {string} -> "184.170.96.196"
|
||||
*/
|
||||
toDottedNotation(bigInt) {
|
||||
toDottedNotation(bigInt: bigint) {
|
||||
if (this.version === 4) {
|
||||
return (
|
||||
[(bigInt >> BigInt(24) & BigInt(255)), (bigInt >> BigInt(16) & BigInt(255)),
|
||||
|
@ -78,15 +82,14 @@ export default class IP {
|
|||
* @return {string} -> 01111111000000000000000000000001
|
||||
*/
|
||||
toBinary() {
|
||||
if (this.integer === 0) {
|
||||
if (this.integer === 0n) {
|
||||
this.toInteger();
|
||||
}
|
||||
let binary = this.integer.toString(2);
|
||||
const v = this.version;
|
||||
const marks = { 4: 32, 6: 128 };
|
||||
const markLen = this.version === 4 ? 32 : 128;
|
||||
|
||||
if (binary.length < marks[v]) {
|
||||
while (binary.length < marks[v]) {
|
||||
if (binary.length < markLen) {
|
||||
while (binary.length < markLen) {
|
||||
binary = `0${binary}`;
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +101,7 @@ export default class IP {
|
|||
* @return {string} -> 7f000001
|
||||
*/
|
||||
toHEX() {
|
||||
if (this.integer === 0) {
|
||||
if (this.integer === 0n) {
|
||||
this.toInteger();
|
||||
}
|
||||
return this.integer.toString(16);
|
||||
|
@ -109,7 +112,7 @@ export default class IP {
|
|||
* IP('127.1.0.0').toCompressed
|
||||
* @return {string} -> "127.1"
|
||||
*/
|
||||
toCompressed(addr, ver) {
|
||||
toCompressed(addr: string, ver: number) {
|
||||
if (ver === 4) {
|
||||
const splittedAddr = addr.split('.');
|
||||
const sRange = [[1, 3], [2, 2], [3, 1], [0, 0]];
|
||||
|
@ -132,11 +135,11 @@ export default class IP {
|
|||
// 'N/A' - _longestZerosGroup fn return in case if there is NO
|
||||
// '0000' blocks in address
|
||||
if (startOfLongest !== 'N/A' || longestLength !== 'N/A') {
|
||||
splitted.splice(startOfLongest, longestLength, '');
|
||||
splitted.splice(Number(startOfLongest), Number(longestLength), '');
|
||||
if (startOfLongest === 0) {
|
||||
splitted.unshift('');
|
||||
}
|
||||
if (startOfLongest + longestLength === 8) {
|
||||
if (Number(startOfLongest) + Number(longestLength) === 8) {
|
||||
splitted.push('');
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +175,7 @@ export default class IP {
|
|||
* @param {string} addr
|
||||
* @return {number} -> 4 or 6
|
||||
*/
|
||||
_checkVersion(addr) {
|
||||
_checkVersion(addr: string) {
|
||||
// matches all possible chars in both versions of IP
|
||||
const reGen = /^[0-9a-f.:]+$/i;
|
||||
if (reGen.test(addr)) {
|
||||
|
@ -184,14 +187,14 @@ export default class IP {
|
|||
const reNum = /^[0-9]+$/;
|
||||
|
||||
if (reNum.test(addr)) {
|
||||
addr = BigInt(addr);
|
||||
if (addr > IPv6MAX || addr <= 0) {
|
||||
const parsedAddr = BigInt(addr);
|
||||
if (parsedAddr > IPv6MAX || parsedAddr <= 0) {
|
||||
throw new Error('Tips: IP address cant be bigger than 2 to the 128-th power or negative number');
|
||||
}
|
||||
else if (addr <= IPv4MAX) {
|
||||
else if (parsedAddr <= IPv4MAX) {
|
||||
return 4;
|
||||
}
|
||||
else if (addr > IPv4MAX) {
|
||||
else if (parsedAddr > IPv4MAX) {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
@ -210,18 +213,14 @@ export default class IP {
|
|||
* @private
|
||||
* @return {string} as a valid address
|
||||
*/
|
||||
_checkAddress(addr, v) {
|
||||
_checkAddress(addr: string, v: number) {
|
||||
const reNum = /^[0-9]+$/;
|
||||
if (reNum.test(addr)) {
|
||||
this.integer = BigInt(addr);
|
||||
return this.toDottedNotation(this.integer);
|
||||
}
|
||||
|
||||
const marks = {
|
||||
4: ['.', this._isIPv4, 4],
|
||||
6: [':', this._isIPv6, 8],
|
||||
};
|
||||
const splittedAddr = addr.split(marks[v][0]);
|
||||
const splittedAddr = addr.split(v === 4 ? '.' : ':');
|
||||
|
||||
if (v === 6 && splittedAddr.length < 8) {
|
||||
const dbColon = (addr.match(/::/g) || []).length;
|
||||
|
@ -230,8 +229,8 @@ export default class IP {
|
|||
}
|
||||
}
|
||||
|
||||
if (marks[v][1].call(this, splittedAddr)) { // TODO: make ifs more readable
|
||||
if (splittedAddr.length === marks[v][2] && this.short === 0) {
|
||||
if ((v === 4 ? this._isIPv4 : this._isIPv6).call(this, splittedAddr)) { // TODO: make ifs more readable
|
||||
if (splittedAddr.length === (v === 4 ? 4 : 8) && this.short === '') {
|
||||
return addr;
|
||||
}
|
||||
else {
|
||||
|
@ -248,16 +247,16 @@ export default class IP {
|
|||
* @private
|
||||
* @return {boolean} whether splitted address is valid IPv6 or not
|
||||
*/
|
||||
_isIPv6(splittedAddr) {
|
||||
_isIPv6(splittedAddr: string[]) {
|
||||
if (splittedAddr.length <= 8) {
|
||||
let checked = false;
|
||||
const [isShort, cleanedAddr] = this._isShort(splittedAddr);
|
||||
|
||||
const regex = /^[0-9a-f]{1,4}$/i;
|
||||
const isValid = function (hextet) {
|
||||
const isValid = function (hextet: string) {
|
||||
return regex.test(hextet);
|
||||
};
|
||||
checked = cleanedAddr.every(isValid);
|
||||
checked = (cleanedAddr as string[]).every(isValid);
|
||||
|
||||
if (checked && isShort) {
|
||||
this.short = splittedAddr.join(':');
|
||||
|
@ -274,13 +273,13 @@ export default class IP {
|
|||
* @private
|
||||
* @return {boolean} whether splitted address is valid IPv4 or not
|
||||
*/
|
||||
_isIPv4(splittedAddr) {
|
||||
_isIPv4(splittedAddr: string[]) {
|
||||
if (splittedAddr.length <= 4) {
|
||||
if (splittedAddr.length < 4) {
|
||||
this.short = splittedAddr.join('.');
|
||||
}
|
||||
const isValid = function (octet) {
|
||||
return (!!((octet <= 255 && octet >= 0)));
|
||||
const isValid = function (octet: string) {
|
||||
return (!!((Number(octet) <= 255 && Number(octet) >= 0)));
|
||||
};
|
||||
return splittedAddr.every(isValid);
|
||||
}
|
||||
|
@ -295,7 +294,7 @@ export default class IP {
|
|||
* @param {array} splittedAddr
|
||||
* @return {array} with both results boolean and cleaned array
|
||||
*/
|
||||
_isShort(splittedAddr) {
|
||||
_isShort(splittedAddr: string[]) {
|
||||
let isShort = false;
|
||||
const cleanedAddr = [...splittedAddr];
|
||||
for (let i = 0; i < cleanedAddr.length; i++) {
|
||||
|
@ -320,7 +319,7 @@ export default class IP {
|
|||
* @param {array} splittedAddr
|
||||
* @return {string} -> "0000:0000:0000:0000:0000:0000:0000:0001"
|
||||
*/
|
||||
_toRepresentation(splittedAddr) {
|
||||
_toRepresentation(splittedAddr: string[]) {
|
||||
if (this.version === 4) {
|
||||
for (let i = 0; i <= 4; i++) {
|
||||
if (splittedAddr[i] === '') {
|
||||
|
@ -383,7 +382,7 @@ export default class IP {
|
|||
* @param {array} zeros
|
||||
* @return {array} -> [0, 7]
|
||||
*/
|
||||
function _longestZerosGroup(splittedAddr) {
|
||||
function _longestZerosGroup(splittedAddr: string[]) {
|
||||
let curr = 0;
|
||||
let currLongest = 0;
|
||||
let startOfLongest = 0;
|
26
src/libs/ip_calculator/ipv4registry.json
Normal file
26
src/libs/ip_calculator/ipv4registry.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
[
|
||||
["0.0.0.0", [8, "This host on this network"]],
|
||||
["10.0.0.0", [8, "Private-Use"]],
|
||||
["100.64.0.0", [10, "Shared Address Space"]],
|
||||
["127.0.0.0", [8, "Loopback"]],
|
||||
["169.254.0.0", [16, "Link Local"]],
|
||||
["172.16.0.0", [12, "Private-Use"]],
|
||||
["192.0.0.0", [24, "IETF Protocol Assignments"]],
|
||||
["192.0.0.0", [29, "IPv4 Service Continuity Prefix"]],
|
||||
["192.0.0.8", [32, "IPv4 dummy address"]],
|
||||
["192.0.0.9", [32, "Port Control Protocol Anycast"]],
|
||||
["192.0.0.10", [32, "Traversal Using Relays around NAT Anycast"]],
|
||||
["192.0.0.170", [32, "NAT64/DNS64 Discovery"]],
|
||||
["192.0.0.171", [32, "NAT64/DNS64 Discovery"]],
|
||||
["192.0.2.0", [24, "Documentation (TEST-NET-1)"]],
|
||||
["192.31.196.0", [24, "AS112-v4"]],
|
||||
["192.52.193.0", [24, "AMT"]],
|
||||
["192.88.99.0", [24, "Deprecated (6to4 Relay Anycast)"]],
|
||||
["192.168.0.0", [16, "Private Use"]],
|
||||
["192.175.48.0", [24, "Direct Delegation AS112 Service"]],
|
||||
["198.18.0.0", [15, "Benchmarking"]],
|
||||
["198.51.100.0", [24, "Documentation (TEST-NET-2)"]],
|
||||
["203.0.113.0", [24, "Documentation (TEST-NET-3)"]],
|
||||
["240.0.0.0", [4, "Reserved"]],
|
||||
["255.255.255.255", [32, "Limited Broadcast"]]
|
||||
]
|
24
src/libs/ip_calculator/ipv6registry.json
Normal file
24
src/libs/ip_calculator/ipv6registry.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
[
|
||||
["::1", [128, "Loopback Address"]],
|
||||
["::", [128, "Unspecified Address"]],
|
||||
["::", [128, "Unspecified Address"]],
|
||||
["::ffff:0:0", [98, "IPv4-mapped Address"]],
|
||||
["64:ff9b::", [96, "IPv4-IPv6 Translat."]],
|
||||
["64:ff9b:1::", [48, "IPv4-IPv6 Translat."]],
|
||||
["100::", [64, "Discard-Only Address Block"]],
|
||||
["2001::", [23, "IETF Protocol Assignments"]],
|
||||
["2001::", [32, "TEREDO"]],
|
||||
["2001:1::1", [128, "Port Control Protocol Anycast"]],
|
||||
["2001:1::2", [128, "Traversal Using Relays around NAT Anycast"]],
|
||||
["2001:2::", [48, "Benchmarking"]],
|
||||
["2001:3::", [32, "AMT"]],
|
||||
["2001:4:112::", [48, "AS112-v6"]],
|
||||
["2001:5::", [32, "EID Space for LISP (Managed by RIPE NCC)"]],
|
||||
["2001:10::", [28, "Deprecated (previously ORCHID)"]],
|
||||
["2001:20::", [28, "ORCHIDv2"]],
|
||||
["2001:db8::", [32, "Documentation"]],
|
||||
["2002::", [16, "6to4"]],
|
||||
["2620:4f:8000::", [48, "Direct Delegation AS112 Service"]],
|
||||
["fc00::", [7, "Unique-Local"]],
|
||||
["fe80::", [10, "Link-Local Unicast"]]
|
||||
]
|
|
@ -1,62 +1,16 @@
|
|||
import IP from './ip.js';
|
||||
import IP from './ip';
|
||||
import ipv4registry from './ipv4registry.json';
|
||||
import ipv6registry from './ipv6registry.json';
|
||||
|
||||
const IPv4MAX = (BigInt(2) ** BigInt(32)) - BigInt(1);
|
||||
const IPv6MAX = (BigInt(2) ** BigInt(128)) - BigInt(1);
|
||||
|
||||
// IP range specific information, see IANA allocations.
|
||||
// http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
||||
const _ipv4Registry = new Map([
|
||||
['0.0.0.0', [8, 'This host on this network']],
|
||||
['10.0.0.0', [8, 'Private-Use']],
|
||||
['100.64.0.0', [10, 'Shared Address Space']],
|
||||
['127.0.0.0', [8, 'Loopback']],
|
||||
['169.254.0.0', [16, 'Link Local']],
|
||||
['172.16.0.0', [12, 'Private-Use']],
|
||||
['192.0.0.0', [24, 'IETF Protocol Assignments']],
|
||||
['192.0.0.0', [29, 'IPv4 Service Continuity Prefix']],
|
||||
['192.0.0.8', [32, 'IPv4 dummy address']],
|
||||
['192.0.0.9', [32, 'Port Control Protocol Anycast']],
|
||||
['192.0.0.10', [32, 'Traversal Using Relays around NAT Anycast']],
|
||||
['192.0.0.170', [32, 'NAT64/DNS64 Discovery']],
|
||||
['192.0.0.171', [32, 'NAT64/DNS64 Discovery']],
|
||||
['192.0.2.0', [24, 'Documentation (TEST-NET-1)']],
|
||||
['192.31.196.0', [24, 'AS112-v4']],
|
||||
['192.52.193.0', [24, 'AMT']],
|
||||
['192.88.99.0', [24, 'Deprecated (6to4 Relay Anycast)']],
|
||||
['192.168.0.0', [16, 'Private Use']],
|
||||
['192.175.48.0', [24, 'Direct Delegation AS112 Service']],
|
||||
['198.18.0.0', [15, 'Benchmarking']],
|
||||
['198.51.100.0', [24, 'Documentation (TEST-NET-2)']],
|
||||
['203.0.113.0', [24, 'Documentation (TEST-NET-3)']],
|
||||
['240.0.0.0', [4, 'Reserved']],
|
||||
['255.255.255.255', [32, 'Limited Broadcast']],
|
||||
]);
|
||||
const _ipv4Registry = new Map(ipv4registry.map(v => [v[0] as string, v[1]]));
|
||||
|
||||
// https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
|
||||
const _ipv6Registry = new Map([
|
||||
['::1', [128, 'Loopback Address']],
|
||||
['::', [128, 'Unspecified Address']],
|
||||
['::', [128, 'Unspecified Address']],
|
||||
['::ffff:0:0', [98, 'IPv4-mapped Address']],
|
||||
['64:ff9b::', [96, 'IPv4-IPv6 Translat.']],
|
||||
['64:ff9b:1::', [48, 'IPv4-IPv6 Translat.']],
|
||||
['100::', [64, 'Discard-Only Address Block']],
|
||||
['2001::', [23, 'IETF Protocol Assignments']],
|
||||
['2001::', [32, 'TEREDO']],
|
||||
['2001:1::1', [128, 'Port Control Protocol Anycast']],
|
||||
['2001:1::2', [128, 'Traversal Using Relays around NAT Anycast']],
|
||||
['2001:2::', [48, 'Benchmarking']],
|
||||
['2001:3::', [32, 'AMT']],
|
||||
['2001:4:112::', [48, 'AS112-v6']],
|
||||
['2001:5::', [32, 'EID Space for LISP (Managed by RIPE NCC)']],
|
||||
['2001:10::', [28, 'Deprecated (previously ORCHID)']],
|
||||
['2001:20::', [28, 'ORCHIDv2']],
|
||||
['2001:db8::', [32, 'Documentation']],
|
||||
['2002::', [16, '6to4']],
|
||||
['2620:4f:8000::', [48, 'Direct Delegation AS112 Service']],
|
||||
['fc00::', [7, 'Unique-Local']],
|
||||
['fe80::', [10, 'Link-Local Unicast']],
|
||||
]);
|
||||
const _ipv6Registry = new Map(ipv6registry.map(v => [v[0] as string, v[1]]));
|
||||
|
||||
/**
|
||||
* Network slice calculations.
|
||||
|
@ -68,11 +22,12 @@ const _ipv6Registry = new Map([
|
|||
*/
|
||||
|
||||
export default class Network extends IP {
|
||||
prefix: bigint;
|
||||
/**
|
||||
* Extends IP class. Calls the parent class IP with the parameters passed to Network.
|
||||
* @constructor
|
||||
*/
|
||||
constructor(address, prefix) {
|
||||
constructor(address: string, prefix: number) {
|
||||
super(address);
|
||||
this.prefix = this._checkPrefix(prefix);
|
||||
}
|
||||
|
@ -84,7 +39,7 @@ export default class Network extends IP {
|
|||
* @private
|
||||
* @return {integer} -> prefix: 25n
|
||||
*/
|
||||
_checkPrefix(prefix) {
|
||||
_checkPrefix(prefix: number) {
|
||||
if (this.version === 4) {
|
||||
if (prefix > 0 && prefix <= 32) {
|
||||
return BigInt(prefix);
|
||||
|
@ -105,10 +60,9 @@ export default class Network extends IP {
|
|||
* @return {string} ->LOOPBACK
|
||||
*/
|
||||
printInfo() {
|
||||
const registry = { 4: _ipv4Registry, 6: _ipv6Registry };
|
||||
const results = [];
|
||||
for (const [addr, info] of registry[this.version].entries()) {
|
||||
const found = this.contains(this.address, addr, info[0]);
|
||||
for (const [addr, info] of (this.version === 4 ? _ipv4Registry : _ipv6Registry).entries()) {
|
||||
const found = this.contains(this.address, addr, Number(info[0]));
|
||||
if (found) {
|
||||
results.unshift(info[1]);
|
||||
}
|
||||
|
@ -230,7 +184,7 @@ export default class Network extends IP {
|
|||
* @param {number} prefix
|
||||
* @return {boolean}
|
||||
*/
|
||||
contains(thisIP, otherIP, prefix) {
|
||||
contains(thisIP: string, otherIP: string, prefix: number) {
|
||||
const other = new Network(otherIP, prefix);
|
||||
const thisNetwork = this.networkToInteger();
|
||||
const otherNetwork = other.networkToInteger();
|
||||
|
@ -257,12 +211,19 @@ export default class Network extends IP {
|
|||
* @return {number} -> 16777214
|
||||
*/
|
||||
networkSize() {
|
||||
const marks = { 4: BigInt(32), 6: BigInt(128) };
|
||||
const size = BigInt(2) ** (marks[this.version] - this.prefix);
|
||||
const size = BigInt(2) ** ((this.version === 4 ? BigInt(32) : BigInt(128)) - this.prefix);
|
||||
|
||||
if (this.version === 4 && this.prefix < BigInt(30)) {
|
||||
return size - BigInt(2);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* networkCount - Returns number of network fo the prefix.
|
||||
* @return {number} -> 16777214
|
||||
*/
|
||||
networkCount() {
|
||||
return this.prefix <= 64 ? (BigInt(2) ** BigInt(64n - this.prefix)).toString() : '';
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue