IP Calculator

Languages:
HTML, CSS, JS
Hours spend:
1

This is my first project on Practics in Cyberpack. I created this util, because in my class we need to calculate IP, Mask, AS, AR by hand. This util can save about 5 minut.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non posuere enim. Aenean tincidunt eros at turpis semper pretium. Sed est odio, suscipit non commodo ut, vehicula non mi. Ut non neque sit amet nisl ultrices auctor. Duis ac lacinia lacus, quis maximus orci. Vivamus molestie tincidunt commodo. Praesent suscipit lectus quis tellus luctus maximus. Vestibulum tempor volutpat mauris. Aliquam erat volutpat. Integer blandit fermentum erat, non vulputate sapien scelerisque et. In tempus, lectus nec dapibus luctus, massa nibh auctor eros, eget auctor eros nisi nec purus. Donec aliquam venenatis lectus in hendrerit. Etiam sed metus quis turpis sagittis lacinia. Maecenas ac leo id mauris lacinia tempus vitae vel tortor.

Ut ac iaculis magna. Nulla enim sem, tempor vel fermentum vel, mattis et nibh. Mauris id condimentum tellus. Etiam vitae gravida augue. In non dolor nunc. Fusce ullamcorper mi gravida, varius metus nec, tempus mauris. Vivamus placerat massa in augue rutrum ornare. Phasellus vehicula lacus ipsum, at sodales nisl sollicitudin vitae. In diam massa, semper ut ante ac, efficitur accumsan enim. Cras maximus congue enim, semper finibus sapien. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

HTML

<input id="input" placeholder="Enter IP/Mask (Ex. 192.168.10.32/28)">
<button id="cidr-btn" class="cidr-btn" type="button">Calculate</button>

CSS

#input {
    width: 80%;
    height: 50px;
    display: flex;
    align-items: center;
    border: 3px solid;
    border-radius: 20px;
    padding: 2px 10px 2px 10px;
    font-size: 32px; text-align: center;
    background-image: linear-gradient(352deg, white, rgb(240, 240, 240));
    color: rgb(50, 50, 50);
}
.cidr-btn {
    padding: 10px 16px;
    margin-left: 10px;
    border-radius: 12px;
    border: 0;
    background-image: linear-gradient(200deg, rgb(30, 230, 230), rgb(210, 210, 210));
    color: rgb(50, 50, 50);
    font-weight:600;
    cursor:pointer;
    font-size:18px;
    display: flex;
    align-items: center;
  }
  .cidr-result { margin-top:16px; max-width:920px; }
  .cidr-table{ width:100%; border-collapse:collapse; font-family:Arial, Helvetica, sans-serif; }
  .cidr-table th, .cidr-table td{ padding:10px; border:2px solid black; text-align:left; vertical-align:middle; }
  .cidr-table th{ background-image: linear-gradient(200deg, rgb(230, 230, 230), rgb(200, 200, 200)); font-weight:700; color: black; }
  .cidr-row-title{ width:220px; font-weight:700; }
  .cidr-binary{ font-family:monospace; display:inline-block; padding:6px; border-radius:6px; }
  .cidr-error{ color:#a80000; margin-top:8px; }
  .cidr-caption{ font-weight:700; margin-bottom:8px; }

JS

(function(){
  let inputEl, btn, result;
  try {
    inputEl = document.getElementById('input');
    btn = document.getElementById('cidr-btn');
    result = document.getElementById('cidr-result');
  } catch(e) {
    return;
  }

  if (!btn || !inputEl) return;

  if(!result){
    result = document.createElement('div');
    result.id = 'cidr-result';
    btn.insertAdjacentElement('afterend', result);
  }

  function ipToInt(ip){
    const parts = ip.trim().split('.');
    if(parts.length !== 4) return null;
    const nums = parts.map(p => {
      if(p === '') return NaN;
      const n = Number(p);
      return Number.isInteger(n) && n >= 0 && n <= 255 ? n : NaN;
    });
    if(nums.some(isNaN)) return null;
    return ((nums[0] * 16777216) + (nums[1] * 65536) + (nums[2] * 256) + nums[3]) >>> 0;
  }
  function intToIp(intVal){
    intVal = intVal >>> 0;
    return [ (intVal >>> 24) & 0xFF, (intVal >>> 16) & 0xFF, (intVal >>> 8) & 0xFF, intVal & 0xFF ].join('.');
  }
  function toBinaryOctets(intVal){
    intVal = intVal >>> 0;
    return [ (intVal >>> 24) & 0xFF, (intVal >>> 16) & 0xFF, (intVal >>> 8) & 0xFF, intVal & 0xFF ]
           .map(n => n.toString(2).padStart(8,'0')).join(' ');
  }
  function prefixToMask(prefix){
    if(prefix < 0 || prefix > 32) return null;
    if(prefix === 0) return 0 >>> 0;
    return (0xFFFFFFFF << (32 - prefix)) >>> 0;
  }

  function parseCIDR(raw){
    if(!raw || typeof raw !== 'string') return { error: 'Empty input' };
    const parts = raw.trim().split('/');
    if(parts.length !== 2) return { error: 'Incorrect format. Use, for example: 192.168.10.32/28' };
    const ip = parts[0].trim();
    const prefix = Number(parts[1]);
    if(!Number.isInteger(prefix) || prefix < 0 || prefix > 32) return { error: 'Mask have to be from 0 to 32' };
    const ipInt = ipToInt(ip);
    if(ipInt === null) return { error: 'Incorrect IP address' };
    return { ip, ipInt, prefix };
  }

  function computeAll({ ip, ipInt, prefix, error }){
    if(error) return { error };
    const mask = prefixToMask(prefix);
    if(mask === null) return { error: 'Mask Error' };
    const network = (ipInt & mask) >>> 0;
    const broadcast = (network | (~mask >>> 0)) >>> 0;
    const hostBits = 32 - prefix;
    const totalAddresses = Math.pow(2, hostBits);
    let usableHosts;
    if(prefix === 32) usableHosts = 1;
    else if(prefix === 31) usableHosts = 2;
    else usableHosts = Math.max(0, totalAddresses - 2);

    let firstUsable = null, lastUsable = null;
    if(prefix === 32){
      firstUsable = lastUsable = network;
    } else if(prefix === 31){
      firstUsable = network;
      lastUsable = broadcast;
    } else {
      firstUsable = (network + 1) >>> 0;
      lastUsable = (broadcast - 1) >>> 0;
    }

    return {
      ip, ipInt, prefix,
      mask, maskDotted: intToIp(mask), maskBinary: toBinaryOctets(mask),
      network, networkDotted: intToIp(network), networkBinary: toBinaryOctets(network),
      broadcast, broadcastDotted: intToIp(broadcast), broadcastBinary: toBinaryOctets(broadcast),
      firstUsable, firstUsableDotted: firstUsable === null ? null : intToIp(firstUsable), firstUsableBinary: firstUsable === null ? null : toBinaryOctets(firstUsable),
      lastUsable, lastUsableDotted: lastUsable === null ? null : intToIp(lastUsable), lastUsableBinary: lastUsable === null ? null : toBinaryOctets(lastUsable),
      totalAddresses, usableHosts
    };
  }

  function makeRow(title, dec, bin){
    return `<tr>
      <td class="cidr-row-title">${title}</td>
      <td>${dec || ''}</td>
      <td><div class="cidr-binary">${bin || ''}</div></td>
    </tr>`;
  }

  function render(computed){
    result.innerHTML = '';
    if(!computed || computed.error){
      const msg = computed && computed.error ? computed.error : 'Calculate Error';
      result.innerHTML = `<div class="cidr-error">${msg}</div>`;
      return;
    }

    const rows = [];
    rows.push(makeRow('IP Address', computed.ip, toBinaryOctets(computed.ipInt)));
    rows.push(makeRow('Subnet Mask', `${computed.maskDotted} (/${computed.prefix})`, computed.maskBinary));
    rows.push(makeRow('Network Address', computed.networkDotted, computed.networkBinary));
    rows.push(makeRow('Broadcast Address', computed.broadcastDotted, computed.broadcastBinary));
    rows.push(makeRow('First Usable IP', computed.firstUsableDotted || '—', computed.firstUsableBinary || '—'));
    rows.push(makeRow('Last Usable IP', computed.lastUsableDotted || '—', computed.lastUsableBinary || '—'));
    const hostsDec = `Usable: ${computed.usableHosts} — Total addresses: ${computed.totalAddresses}`;
    const hostsBin = `Usable: ${computed.usableHosts.toString(2)} — Total: ${computed.totalAddresses.toString(2)}`;
    rows.push(`<tr><td class="cidr-row-title">Number of Hosts</td><td colspan="2">${hostsDec} <br/><small style="color:#555">(${hostsBin})</small></td></tr>`);

    const table = `
      <div class="cidr-caption">Result for ${computed.ip}/${computed.prefix}</div>
      <table class="cidr-table" role="table" aria-label="CIDR result">
        <thead><tr><th></th><th>System dziesiętny</th><th>System dwójkowy</th></tr></thead>
        <tbody>${rows.join('')}</tbody>
      </table>
    `;
    result.innerHTML = table;
  }

  function doCalc(){
    const raw = inputEl.value || '';
    const parsed = parseCIDR(raw);
    if(parsed.error){ render(parsed); return; }
    const computed = computeAll(parsed);
    render(computed);
  }

  btn.addEventListener('click', doCalc);
  inputEl.addEventListener('keydown', function(e){ if(e.key === 'Enter') doCalc(); });

  if(inputEl.value && inputEl.value.trim().length) {
    setTimeout(doCalc, 50);
  }

})();