Source: zhelper.js

/*
 * Copyright (c) 2017 Sebastian Rager
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

const os = require('os');
const net = require('net');

/**
 * ZHelper contains some necessary static helper methods.
 */
class ZHelper {
  /**
   * @typedef {object} IfaceData
   * @property {string} address - IP address
   * @property {string} netmask - Netmask
   * @property {string} family - IPv4 or IPv6
   * @property {string} mac - MAC address
   * @property {boolean} internal - Internal or external
   * @property {string} network - Network address
   * @property {string} broadcast - Broadcast address
   */

  /**
   * Returns IPv4 interface data from the given interface or IPv4 address, or searches for a public
   * interface with an assigned IPv4. Also calculates the network and broadcast address of the
   * interface.
   *
   * @param {string} [iface] - Optional interface or IPv4 address to check
   * @return {IfaceData}
   */
  static getIfData(iface) {
    const ifaces = os.networkInterfaces();
    if (typeof ifaces === 'undefined') return undefined;

    // Iterate over all interfaces if no interface has been passed
    if (typeof iface === 'undefined') {
      let ifdata;

      Object.keys(ifaces).some((i) => {
        const x = this.getIfData(i);
        if (typeof x !== 'undefined' && !x.internal) ifdata = x;
        return typeof ifdata !== 'undefined';
      });

      return ifdata;
    }

    const selIface = ifaces[iface];

    // Check if an address has been passed as iface, if the selected iface could not be found
    if (typeof selIface === 'undefined') {
      let ifdata;

      Object.keys(ifaces).some((i) => {
        const x = this.getIfData(i);
        if (typeof x !== 'undefined' && x.address === iface) ifdata = x;
        return typeof ifdata !== 'undefined';
      });

      return ifdata;
    }

    let ifdata;

    Object.keys(selIface).some((i) => {
      if (selIface[i].family === 'IPv4') ifdata = selIface[i];
      return typeof ifdata !== 'undefined';
    });

    if (typeof ifdata === 'undefined') return undefined;

    const addressArr = ifdata.address.split('.');
    const netmaskArr = ifdata.netmask.split('.');
    let network = '';
    let broadcast = '';

    addressArr.forEach((e, i) => {
      network += (e & netmaskArr[i]);
      broadcast += (e | (netmaskArr[i] ^ 255));
      if (i < addressArr.length - 1) {
        network += '.';
        broadcast += '.';
      }
    });

    ifdata.network = network;
    ifdata.broadcast = broadcast;

    return ifdata;
  }

  /**
   * Checks if a given IP is in the subnet of the given network address and netmask.
   *
   * @param {string} ip - Ip that should be checked
   * @param {string} network - Network address
   * @param {string} netmask - Netmask
   * @return {boolean}
   */
  static ipInSubnet(ip, network, netmask) {
    const ipArr = ip.split('.');
    const netmaskArr = netmask.split('.');
    let calcNet = '';

    ipArr.forEach((e, i) => {
      calcNet += e & netmaskArr[i];
      if (i < ipArr.length - 1) calcNet += '.';
    });

    return calcNet === network;
  }

  /**
   * Finds a free TCP port starting from the given port, incremented by one.
   *
   * @param {string} address - Address to search on
   * @param {number} port - Port to start searching from
   * @return {Promise.<port>}
   */
  static getFreePort(address, port) {
    const server = net.createServer();

    return new Promise((resolve) => {
      server.on('error', () => {
        server.close(() => {
          this.getFreePort(address, port + 1).then((portRec) => {
            resolve(portRec);
          });
        });
      });

      server.listen(port, address, () => {
        server.close(() => {
          resolve(port);
        });
      });
    });
  }
}

module.exports = ZHelper;