// --- local functions --- let expandInp = document.getElementById('address_to_expand'); expandInp.oninput = expandChanged; let compressInp = document.getElementById('address_to_compress'); compressInp.oninput = compressChanged; let ptrInp = document.getElementById('ptr_to_generate'); ptrInp.oninput = ptrChanged; let randomInput = document.getElementById('random_subnet_src'); randomInput.oninput = randomChanged; randomInput.addEventListener("keyup", function(event) { if (event.keyCode === 13) { event.preventDefault(); randomChanged(); } }); let subnetInput = document.getElementById('subnet_in'); subnetInput.oninput = subnetChanged; $(document).ready(function() { randomChanged(); }); function expandChanged(e){ try { $("#expanded_address").text(normalize($("#address_to_expand").val())); $("#address_to_expand").parent().removeClass("has-warning"); } catch { $("#address_to_expand").parent().addClass("has-warning"); } } function compressChanged(e){ try { $("#compressed_address").text(abbreviate($("#address_to_compress").val())); $("#address_to_compress").parent().removeClass("has-warning"); } catch { $("#address_to_compress").parent().addClass("has-warning"); } } function ptrChanged(e){ try { $("#generated_ptr").text(ptr($("#ptr_to_generate").val())); $("#ptr_to_generate").parent().removeClass("has-warning"); } catch { $("#ptr_to_generate").parent().addClass("has-warning"); } } function randomChanged(e){ try { split = $("#random_subnet_src").val().split("/"); $("#random_subnet").text(randomSubnet(split[0], split[1], split[2], 1, true)); $("#random_subnet_src").parent().removeClass("has-warning"); } catch { $("#random_subnet_src").parent().addClass("has-warning"); } } function subnetChanged(e){ try { split = $("#subnet_in").val().split("/"); var out = ""; var r = range(split[0], split[1], 128); out += "Network Range: \n" out += formatAddress(r.start, split[1]) + "\n"; out += formatAddress(r.end, split[1]) + "\n\n"; if (split[1] >= 64){ out += r.size + " addresses\n"; } else if (split[1] >= 48){ out += (r.size / 18446744073709552000) + " /64 subnets\n" } else { out += (r.size / 18446744073709552000 / 65536) + " /48 subnets\n" } $("#subnet_out").html(out); $("#subnet_in").parent().removeClass("has-warning"); } catch { $("#subnet_in").parent().addClass("has-warning"); } } // --- local helper functions --- function formatAddress(fulladdr, mask){ fulladdr = normalize(fulladdr).replaceAll(":",""); last = 32-Math.floor((128-mask)/4); net = fulladdr.substr(0,last); addr = fulladdr.substr(last) both = ""; tmpnet = ""; tmpaddr = ""; for (i = 0; i < last; i++){ tmpnet+=net[i]; if (i%4==3) { tmpnet+=":"; } } for (i = last; i < last+(32-last); i++){ tmpaddr+=addr[i-last]; if (i%4==3 && i != (last+(32-last)-1)) { tmpaddr+=":"; } } net = tmpnet; addr = tmpaddr; if (net.endsWith(":")){ net = net.substring(0, net.length-1); if (mask <=124) { addr = ":" + addr; } } if (mask%4 == 0){ return '' + net + '' + addr + '' } else { both = net[net.length - 1]; net = net.substring(0, net.length-1); return '' + net + '' + both + '' + addr + '' } } // --- library functions --- // adapted under MIT from https://github.com/elgs/ip6, Copyright (c) 2016 Qian Chen const normalize = function (a) { validate(a); a = a.toLowerCase() const nh = a.split(/\:\:/g); if (nh.length > 2) { throw new Error('Invalid address: ' + a); } let sections = []; if (nh.length === 1) { // full mode sections = a.split(/\:/g); if (sections.length !== 8) { throw new Error('Invalid address: ' + a); } } else if (nh.length === 2) { // compact mode const n = nh[0]; const h = nh[1]; const ns = n.split(/\:/g); const hs = h.split(/\:/g); for (let i in ns) { sections[i] = ns[i]; } for (let i = hs.length; i > 0; --i) { sections[7 - (hs.length - i)] = hs[i - 1]; } } for (let i = 0; i < 8; ++i) { if (sections[i] === undefined) { sections[i] = '0000'; } sections[i] = _leftPad(sections[i], '0', 4); } return sections.join(':'); }; const abbreviate = function (a) { validate(a); a = normalize(a); a = a.replace(/0000/g, 'g'); a = a.replace(/\:000/g, ':'); a = a.replace(/\:00/g, ':'); a = a.replace(/\:0/g, ':'); a = a.replace(/g/g, '0'); const sections = a.split(/\:/g); let zPreviousFlag = false; let zeroStartIndex = -1; let zeroLength = 0; let zStartIndex = -1; let zLength = 0; for (let i = 0; i < 8; ++i) { const section = sections[i]; let zFlag = (section === '0'); if (zFlag && !zPreviousFlag) { zStartIndex = i; } if (!zFlag && zPreviousFlag) { zLength = i - zStartIndex; } if (zLength > 1 && zLength > zeroLength) { zeroStartIndex = zStartIndex; zeroLength = zLength; } zPreviousFlag = (section === '0'); } if (zPreviousFlag) { zLength = 8 - zStartIndex; } if (zLength > 1 && zLength > zeroLength) { zeroStartIndex = zStartIndex; zeroLength = zLength; } //console.log(zeroStartIndex, zeroLength); //console.log(sections); if (zeroStartIndex >= 0 && zeroLength > 1) { sections.splice(zeroStartIndex, zeroLength, 'g'); } //console.log(sections); a = sections.join(':'); //console.log(a); a = a.replace(/\:g\:/g, '::'); a = a.replace(/\:g/g, '::'); a = a.replace(/g\:/g, '::'); a = a.replace(/g/g, '::'); //console.log(a); return a; }; // Basic validation const validate = function (a) { const ns = []; const nh = a.split('::'); if (nh.length > 2) { throw new Error('Invalid address: ' + a); } else if (nh.length === 2) { if (nh[0].startsWith(':') || nh[0].endsWith(':') || nh[1].startsWith(':') || nh[1].endsWith(':')) { throw new Error('Invalid address: ' + a); } ns.push(... (nh[0].split(':').filter(a => a))); ns.push(... (nh[1].split(':').filter(a => a))); if (ns.length > 7) { throw new Error('Invalid address: ' + a); } } else if (nh.length === 1) { ns.push(... (nh[0].split(':').filter(a => a))); if (ns.length !== 8) { throw new Error('Invalid address: ' + a); } } for (const n of ns) { const match = n.match(/^[a-f0-9]{1,4}$/i); if (!match || match[0] !== n) { throw new Error('Invalid address: ' + a); } } }; const _leftPad = function (d, p, n) { const padding = p.repeat(n); if (d.length < padding.length) { d = padding.substring(0, padding.length - d.length) + d; } return d; }; const _hex2bin = function (hex) { return parseInt(hex, 16).toString(2) }; const _bin2hex = function (bin) { return parseInt(bin, 2).toString(16) }; const _addr2bin = function (addr) { const nAddr = normalize(addr); const sections = nAddr.split(":"); let binAddr = ''; for (const section of sections) { binAddr += _leftPad(_hex2bin(section), '0', 16); } return binAddr; }; const _bin2addr = function (bin) { const addr = []; for (let i = 0; i < 8; ++i) { const binPart = bin.substr(i * 16, 16); const hexSection = _leftPad(_bin2hex(binPart), '0', 4); addr.push(hexSection); } return addr.join(':'); }; const divideSubnet = function (addr, mask0, mask1, limit, abbr) { validate(addr); mask0 *= 1; mask1 *= 1; limit *= 1; mask1 = mask1 || 128; if (mask0 < 1 || mask1 < 1 || mask0 > 128 || mask1 > 128 || mask0 > mask1) { throw new Error('Invalid masks.'); } const ret = []; const binAddr = _addr2bin(addr); const binNetPart = binAddr.substr(0, mask0); const binHostPart = '0'.repeat(128 - mask1); const numSubnets = Math.pow(2, mask1 - mask0); for (let i = 0; i < numSubnets; ++i) { if (!!limit && i >= limit) { break; } const binSubnet = _leftPad(i.toString(2), '0', mask1 - mask0); const binSubAddr = binNetPart + binSubnet + binHostPart; const hexAddr = _bin2addr(binSubAddr); if (!!abbr) { ret.push(abbreviate(hexAddr)); } else { ret.push(hexAddr); } } // console.log(numSubnets); // console.log(binNetPart, binSubnetPart, binHostPart); // console.log(binNetPart.length, binSubnetPart.length, binHostPart.length); // console.log(ret.length); return ret; }; const range = function (addr, mask0, mask1, abbr) { validate(addr); mask0 *= 1; mask1 *= 1; mask1 = mask1 || 128; if (mask0 < 1 || mask1 < 1 || mask0 > 128 || mask1 > 128 || mask0 > mask1) { throw new Error('Invalid masks.'); } const binAddr = _addr2bin(addr); const binNetPart = binAddr.substr(0, mask0); const binHostPart = '0'.repeat(128 - mask1); const binStartAddr = binNetPart + '0'.repeat(mask1 - mask0) + binHostPart; const binEndAddr = binNetPart + '1'.repeat(mask1 - mask0) + binHostPart; if (!!abbr) { return { start: abbreviate(_bin2addr(binStartAddr)), end: abbreviate(_bin2addr(binEndAddr)), size: Math.pow(2, mask1 - mask0) }; } else { return { start: _bin2addr(binStartAddr), end: _bin2addr(binEndAddr), size: Math.pow(2, mask1 - mask0) }; } }; const rangeBigInt = function (addr, mask0, mask1, abbr) { if (typeof BigInt === 'undefined') { return range(addr, mask0, mask1, abbr); } validate(addr); mask0 *= 1; mask1 *= 1; mask1 = mask1 || 128; if (mask0 < 1 || mask1 < 1 || mask0 > 128 || mask1 > 128 || mask0 > mask1) { throw new Error('Invalid masks.'); } const binAddr = _addr2bin(addr); const binNetPart = binAddr.substr(0, mask0); const binHostPart = '0'.repeat(128 - mask1); const binStartAddr = binNetPart + '0'.repeat(mask1 - mask0) + binHostPart; const binEndAddr = binNetPart + '1'.repeat(mask1 - mask0) + binHostPart; if (!!abbr) { return { start: abbreviate(_bin2addr(binStartAddr)), end: abbreviate(_bin2addr(binEndAddr)), size: BigInt(2 ** (mask1 - mask0)).toString() }; } else { return { start: _bin2addr(binStartAddr), end: _bin2addr(binEndAddr), size: BigInt(2 ** (mask1 - mask0)).toString() }; } }; const randomSubnet = function (addr, mask0, mask1, limit, abbr) { validate(addr); mask0 *= 1; mask1 *= 1; limit *= 1; mask1 = mask1 || 128; limit = limit || 1; if (mask0 < 1 || mask1 < 1 || mask0 > 128 || mask1 > 128 || mask0 > mask1) { throw new Error('Invalid masks.'); } const ret = []; const binAddr = _addr2bin(addr); const binNetPart = binAddr.substr(0, mask0); const binHostPart = '0'.repeat(128 - mask1); const numSubnets = Math.pow(2, mask1 - mask0); for (let i = 0; i < numSubnets && i < limit; ++i) { // generate an binary string with length of mask1 - mask0 let binSubnet = ''; for (let j = 0; j < mask1 - mask0; ++j) { binSubnet += Math.floor(Math.random() * 2); } const binSubAddr = binNetPart + binSubnet + binHostPart; const hexAddr = _bin2addr(binSubAddr); if (!!abbr) { ret.push(abbreviate(hexAddr)); } else { ret.push(hexAddr); } } // console.log(numSubnets); // console.log(binNetPart, binSubnetPart, binHostPart); // console.log(binNetPart.length, binSubnetPart.length, binHostPart.length); // console.log(ret.length); return ret + "/" + mask1; }; const ptr = function (addr) { validate(addr); const fullAddr = normalize(addr); const reverse = fullAddr.replace(/:/g, '').split('').reverse(); return reverse.slice(0, 128 / 4).join('.') + ".ip6.arpa"; }; if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { exports.validate = validate; exports.normalize = normalize; exports.abbreviate = abbreviate; exports.divideSubnet = divideSubnet; exports.range = range; exports.rangeBigInt = rangeBigInt; exports.randomSubnet = randomSubnet; exports.ptr = ptr; } else { window.ip6_validate = validate; window.ip6_normalize = normalize; window.ip6_abbreviate = abbreviate; window.ip6_divideSubnet = divideSubnet; window.ip6_range = range; window.ip6_rangeBigInt = rangeBigInt; window.ip6_randomSubnet = randomSubnet; window.ip6_ptr = ptr; }