text: 'use strict';
  // @ts-check
  // ==================================================================================
  // network.js
  // ----------------------------------------------------------------------------------
  // Description:   System Information - library
  //                for Node.js
  // Copyright:     (c) 2014 - 2020
  // Author:        Sebastian Hildebrandt
  // ----------------------------------------------------------------------------------
  // License:       MIT
  // ==================================================================================
  // 9. Network
  // ----------------------------------------------------------------------------------
  
  const os = require('os');
  const exec = require('child_process').exec;
  const execSync = require('child_process').execSync;
  const fs = require('fs');
  const util = require('./util');
  
  let _platform = process.platform;
  
  const _linux = (_platform === 'linux');
  const _darwin = (_platform === 'darwin');
  const _windows = (_platform === 'win32');
  const _freebsd = (_platform === 'freebsd');
  const _openbsd = (_platform === 'openbsd');
  const _netbsd = (_platform === 'netbsd');
  const _sunos = (_platform === 'sunos');
  
  let _network = {};
  let _default_iface = '';
  let _ifaces = {};
  let _dhcpNics = [];
  let _networkInterfaces = [];
  let _mac = {};
  let pathToIp;
  
  function getDefaultNetworkInterface() {
  
    let ifaces = os.networkInterfaces();
    let ifacename = '';
    let ifacenameFirst = '';
  
    let scopeid = 9999;
  
    // fallback - "first" external interface (sorted by scopeid)
    for (let dev in ifaces) {
      if ({}.hasOwnProperty.call(ifaces, dev)) {
        ifaces[dev].forEach(function (details) {
          if (details && details.internal === false) {
            ifacenameFirst = ifacenameFirst || dev; // fallback if no scopeid
            if (details.scopeid && details.scopeid < scopeid) {
              ifacename = dev;
              scopeid = details.scopeid;
            }
          }
        });
      }
    }
    ifacename = ifacename || ifacenameFirst || '';
  
    try {
      if (_windows) {
        // https://www.inetdaemon.com/tutorials/internet/ip/routing/default_route.shtml
        let defaultIp = '';
        const cmd = 'netstat -r';
        const result = execSync(cmd);
        const lines = result.toString().split(os.EOL);
        lines.forEach(line => {
          line = line.replace(/\s+/g, ' ').trim();
          if (line.indexOf('0.0.0.0 0.0.0.0') > -1 && !(/[a-zA-Z]/.test(line))) {
            const parts = line.split(' ');
            if (parts.length >= 5) {
              defaultIp = parts[parts.length - 2];
            }
          }
        });
        if (defaultIp) {
          for (let dev in ifaces) {
            if ({}.hasOwnProperty.call(ifaces, dev)) {
              ifaces[dev].forEach(function (details) {
                if (details && details.address && details.address === defaultIp) {
                  ifacename = dev;
                }
              });
            }
          }
        }
      }
      if (_linux) {
        let cmd = 'ip route 2> /dev/null | grep default';
        let result = execSync(cmd);
        let parts = result.toString().split('\n')[0].split(/\s+/);
        if (parts[0] === 'none' && parts[5]) {
          ifacename = parts[5];
        } else if (parts[4]) {
          ifacename = parts[4];
        }
  
        if (ifacename.indexOf(':') > -1) {
          ifacename = ifacename.split(':')[1].trim();
        }
      }
      if (_darwin || _freebsd || _openbsd || _netbsd || _sunos) {
        let cmd = '';
        if (_linux) cmd = 'ip route 2> /dev/null | grep default | awk \'{print $5}\'';
        if (_darwin) cmd = 'route get 0.0.0.0 2>/dev/null | grep interface: | awk \'{print $2}\'';
        if (_freebsd || _openbsd || _netbsd || _sunos) cmd = 'route get 0.0.0.0 | grep interface:';
        let result = execSync(cmd);
        ifacename = result.toString().split('\n')[0];
        if (ifacename.indexOf(':') > -1) {
          ifacename = ifacename.split(':')[1].trim();
        }
      }
    } catch (e) {
      util.noop();
    }
    if (ifacename) _default_iface = ifacename;
    return _default_iface;
  }
  
  exports.getDefaultNetworkInterface = getDefaultNetworkInterface;
  
  function getMacAddresses() {
    let iface = '';
    let mac = '';
    let result = {};
    if (_linux || _freebsd || _openbsd || _netbsd) {
      if (typeof pathToIp === 'undefined') {
        try {
          const lines = execSync('which ip').toString().split('\n');
          if (lines.length && lines[0].indexOf(':') === -1 && lines[0].indexOf('/') === 0) {
            pathToIp = lines[0];
          } else {
            pathToIp = '';
          }
        } catch (e) {
          pathToIp = '';
        }
      }
      try {
        const cmd = 'export LC_ALL=C; ' + ((pathToIp) ? pathToIp + ' link show up' : '/sbin/ifconfig') + '; unset LC_ALL';
        let res = execSync(cmd);
        const lines = res.toString().split('\n');
        for (let i = 0; i < lines.length; i++) {
          if (lines[i] && lines[i][0] !== ' ') {
            if (pathToIp) {
              let nextline = lines[i + 1].trim().split(' ');
              if (nextline[0] === 'link/ether') {
                iface = lines[i].split(' ')[1];
                iface = iface.slice(0, iface.length - 1);
                mac = nextline[1];
              }
            } else {
              iface = lines[i].split(' ')[0];
              mac = lines[i].split('HWaddr ')[1];
            }
  
            if (iface && mac) {
              result[iface] = mac.trim();
              iface = '';
              mac = '';
            }
          }
        }
      } catch (e) {
        util.noop();
      }
    }
    if (_darwin) {
      try {
        const cmd = '/sbin/ifconfig';
        let res = execSync(cmd);
        const lines = res.toString().split('\n');
        for (let i = 0; i < lines.length; i++) {
          if (lines[i] && lines[i][0] !== '\t' && lines[i].indexOf(':') > 0) {
            iface = lines[i].split(':')[0];
          } else if (lines[i].indexOf('\tether ') === 0) {
            mac = lines[i].split('\tether ')[1];
            if (iface && mac) {
              result[iface] = mac.trim();
              iface = '';
              mac = '';
            }
          }
        }
      } catch (e) {
        util.noop();
      }
    }
    return result;
  }
  
  function networkInterfaceDefault(callback) {
  
    return new Promise((resolve) => {
      process.nextTick(() => {
        let result = getDefaultNetworkInterface();
        if (callback) { callback(result); }
        resolve(result);
      });
    });
  }
  
  exports.networkInterfaceDefault = networkInterfaceDefault;
  
  // --------------------------
  // NET - interfaces
  
  function parseLinesWindowsNics(sections, nconfigsections) {
    let nics = [];
    for (let i in sections) {
      if ({}.hasOwnProperty.call(sections, i)) {
  
        if (sections[i].trim() !== '') {
  
          let lines = sections[i].trim().split('\r\n');
          let linesNicConfig = nconfigsections[i].trim().split('\r\n');
          let netEnabled = util.getValue(lines, 'NetEnabled', '=');
  
          if (netEnabled !== '') {
            const speed = parseInt(util.getValue(lines, 'speed', '=').trim(), 10) / 1000000;
            nics.push({
              mac: util.getValue(lines, 'MACAddress', '=').toLowerCase(),
              dhcp: util.getValue(linesNicConfig, 'dhcpEnabled', '=').toLowerCase(),
              name: util.getValue(lines, 'Name', '=').replace(/\]/g, ')').replace(/\[/g, '('),
              netEnabled: netEnabled === 'TRUE',
              speed: isNaN(speed) ? -1 : speed,
              operstate: util.getValue(lines, 'NetConnectionStatus', '=') === '2' ? 'up' : 'down',
              type: util.getValue(lines, 'AdapterTypeID', '=') === '9' ? 'wireless' : 'wired'
            });
          }
        }
      }
    }
    return nics;
  }
  
  function getWindowsNics() {
    const cmd = util.getWmic() + ' nic get MACAddress, name, NetEnabled, Speed, NetConnectionStatus, AdapterTypeId /value';
    const cmdnicconfig = util.getWmic() + ' nicconfig get dhcpEnabled /value';
    try {
      const nsections = execSync(cmd, util.execOptsWin).split(/\n\s*\n/);
      const nconfigsections = execSync(cmdnicconfig, util.execOptsWin).split(/\n\s*\n/);
      return (parseLinesWindowsNics(nsections, nconfigsections));
    } catch (e) {
      return [];
    }
  }
  
  function getWindowsDNSsuffixes() {
  
    let iface = {};
  
    let dnsSuffixes = {
      primaryDNS: '',
      exitCode: 0,
      ifaces: [],
    };
  
    try {
      const ipconfig = execSync('ipconfig /all', util.execOptsWin);
      const ipconfigArray = ipconfig.split('\r\n\r\n');
  
      ipconfigArray.forEach((element, index) => {
  
        if (index == 1) {
          const longPrimaryDNS = element.split('\r\n').filter((element) => {
            return element.toUpperCase().includes('DNS');
          });
          const primaryDNS = longPrimaryDNS[0].substring(longPrimaryDNS[0].lastIndexOf(':') + 1);
          dnsSuffixes.primaryDNS = primaryDNS.trim();
          if (!dnsSuffixes.primaryDNS) dnsSuffixes.primaryDNS = 'Not defined';
        }
        if (index > 1) {
          if (index % 2 == 0) {
            const name = element.substring(element.lastIndexOf(' ') + 1).replace(':', '');
            iface.name = name;
          } else {
            const connectionSpecificDNS = element.split('\r\n').filter((element) => {
              return element.toUpperCase().includes('DNS');
            });
            const dnsSuffix = connectionSpecificDNS[0].substring(connectionSpecificDNS[0].lastIndexOf(':') + 1);
            iface.dnsSuffix = dnsSuffix.trim();
            dnsSuffixes.ifaces.push(iface);
            iface = {};
          }
        }
      });
  
      return dnsSuffixes;
    } catch (error) {
      // console.log('An error occurred trying to bring the Connection-specific DNS suffix', error.message);
      return {
        primaryDNS: '',
        exitCode: 0,
        ifaces: [],
      };
    }
  }
  
  function getWindowsIfaceDNSsuffix(ifaces, ifacename) {
    let dnsSuffix = '';
    // Adding (.) to ensure ifacename compatibility when duplicated iface-names
    const interfaceName = ifacename + '.';
    try {
      const connectionDnsSuffix = ifaces.filter((iface) => {
        return interfaceName.includes(iface.name + '.');
      }).map((iface) => iface.dnsSuffix);
      if (connectionDnsSuffix[0]) {
        dnsSuffix = connectionDnsSuffix[0];
      }
      if (!dnsSuffix) dnsSuffix = '';
      return dnsSuffix;
    } catch (error) {
      // console.log('Error getting Connection-specific DNS suffix: ', error.message);
      return 'Unknown';
    }
  }
  
  function getWindowsWiredProfilesInformation() {
    try {
      const result = execSync('netsh lan show profiles', util.execOptsWin);
      const profileList = result.split('\r\nProfile on interface');
      return profileList;
    } catch (error) {
      if (error.status === 1 && error.stdout.includes('AutoConfig')) {
        return 'Disabled';
      }
      return [];
    }
  }
  
  function getWindowsWirelessIfaceSSID(interfaceName) {
    try {
      const result = execSync(`netsh wlan show  interface name="${interfaceName}" | findstr "SSID"`, util.execOptsWin);
      const SSID = result.split('\r\n').shift();
      const parseSSID = SSID.split(':').pop();
      return parseSSID;
    } catch (error) {
      return 'Unknown';
    }
  }
  function getWindowsIEEE8021x(connectionType, iface, ifaces) {
    let i8021x = {
      state: 'Unknown',
      protocol: 'Unknown',
    };
  
    if (ifaces === 'Disabled') {
      i8021x.state = 'Disabled';
      i8021x.protocol = 'Not defined';
      return i8021x;
    }
  
    if (connectionType == 'wired' && ifaces.length > 0) {
      try {
        // Get 802.1x information by interface name
        const iface8021xInfo = ifaces.find((element) => {
          return element.includes(iface + '\r\n');
        });
        const arrayIface8021xInfo = iface8021xInfo.split('\r\n');
        const state8021x = arrayIface8021xInfo.find((element) => {
          return element.includes('802.1x');
        });
  
        if (state8021x.includes('Disabled')) {
          i8021x.state = 'Disabled';
          i8021x.protocol = 'Not defined';
        } else if (state8021x.includes('Enabled')) {
          const protocol8021x = arrayIface8021xInfo.find((element) => {
            return element.includes('EAP');
          });
          i8021x.protocol = protocol8021x.split(':').pop();
          i8021x.state = 'Enabled';
        }
      } catch (error) {
        // console.log('Error getting wired information:', error);
        return i8021x;
      }
    } else if (connectionType == 'wireless') {
  
      let i8021xState = '';
      let i8021xProtocol = '';
  
  
  
      try {
        const SSID = getWindowsWirelessIfaceSSID(iface);
        if (SSID !== 'Unknown') {
          i8021xState = execSync(`netsh wlan show profiles "${SSID}" | findstr "802.1X"`, util.execOptsWin);
          i8021xProtocol = execSync(`netsh wlan show profiles "${SSID}" | findstr "EAP"`, util.execOptsWin);
        }
  
        if (i8021xState.includes(':') && i8021xProtocol.includes(':')) {
          i8021x.state = i8021xState.split(':').pop();
          i8021x.protocol = i8021xProtocol.split(':').pop();
        }
      } catch (error) {
        // console.log('Error getting wireless information:', error);
        if (error.status === 1 && error.stdout.includes('AutoConfig')) {
          i8021x.state = 'Disabled';
          i8021x.protocol = 'Not defined';
        }
        return i8021x;
      }
    }
  
    return i8021x;
  }
  
  function splitSectionsNics(lines) {
    const result = [];
    let section = [];
    lines.forEach(function (line) {
      if (!line.startsWith('\t') && !line.startsWith(' ')) {
        if (section.length) {
          result.push(section);
          section = [];
        }
      }
      section.push(line);
    });
    if (section.length) {
      result.push(section);
    }
    return result;
  }
  
  function parseLinesDarwinNics(sections) {
    let nics = [];
    sections.forEach(section => {
      let nic = {
        iface: '',
        mtu: -1,
        mac: '',
        ip6: '',
        ip4: '',
        speed: -1,
        type: '',
        operstate: '',
        duplex: '',
        internal: false
      };
      const first = section[0];
      nic.iface = first.split(':')[0].trim();
      let parts = first.split('> mtu');
      nic.mtu = parts.length > 1 ? parseInt(parts[1], 10) : -1;
      if (isNaN(nic.mtu)) {
        nic.mtu = -1;
      }
      nic.internal = parts[0].indexOf('LOOPBACK') > -1;
      section.forEach(line => {
        if (line.trim().startsWith('ether ')) {
          nic.mac = line.split('ether ')[1].toLowerCase().trim();
        }
        if (line.trim().startsWith('inet6 ') && !nic.ip6) {
          nic.ip6 = line.split('inet6 ')[1].toLowerCase().split('%')[0].split(' ')[0];
        }
        if (line.trim().startsWith('inet ') && !nic.ip4) {
          nic.ip4 = line.split('inet ')[1].toLowerCase().split(' ')[0];
        }
      });
      let speed = util.getValue(section, 'link rate');
      nic.speed = speed ? parseFloat(speed) : -1;
      if (nic.speed === -1) {
        speed = util.getValue(section, 'uplink rate');
        nic.speed = speed ? parseFloat(speed) : -1;
        if (nic.speed > -1 && speed.toLowerCase().indexOf('gbps') >= 0) {
          nic.speed = nic.speed * 1000;
        }
      } else {
        if (speed.toLowerCase().indexOf('gbps') >= 0) {
          nic.speed = nic.speed * 1000;
        }
      }
      nic.type = util.getValue(section, 'type').toLowerCase().indexOf('wi-fi') > -1 ? 'wireless' : 'wired';
      nic.operstate = util.getValue(section, 'status').toLowerCase().indexOf('active') > -1 ? 'up' : 'down';
      nic.duplex = util.getValue(section, 'media').toLowerCase().indexOf('half-duplex') > -1 ? 'half' : 'full';
      if (nic.ip6 || nic.ip4 || nic.mac) {
        nics.push(nic);
      }
    });
    return nics;
  }
  
  function getDarwinNics() {
    const cmd = '/sbin/ifconfig -v';
    try {
      const lines = execSync(cmd, { maxBuffer: 1024 * 20000 }).toString().split('\n');
      const nsections = splitSectionsNics(lines);
      return (parseLinesDarwinNics(nsections));
    } catch (e) {
      return [];
    }
  }
  
  function getLinuxIfaceConnectionName(interfaceName) {
    const cmd = `nmcli device status 2>/dev/null | grep ${interfaceName}`;
  
    try {
      const result = execSync(cmd).toString();
      const resultFormat = result.replace(/\s+/g, ' ').trim();
      const connectionNameLines = resultFormat.split(' ').slice(3);
      const connectionName = connectionNameLines.join(' ');
      return connectionName != '--' ? connectionName : '';
    } catch (e) {
      return '';
    }
  }
  
  function checkLinuxDCHPInterfaces(file) {
    let result = [];
    try {
      let cmd = `cat ${file} 2> /dev/null | grep 'iface\\|source'`;
      const lines = execSync(cmd, { maxBuffer: 1024 * 20000 }).toString().split('\n');
  
      lines.forEach(line => {
        const parts = line.replace(/\s+/g, ' ').trim().split(' ');
        if (parts.length >= 4) {
          if (line.toLowerCase().indexOf(' inet ') >= 0 && line.toLowerCase().indexOf('dhcp') >= 0) {
            result.push(parts[1]);
          }
        }
        if (line.toLowerCase().includes('source')) {
          let file = line.split(' ')[1];
          result = result.concat(checkLinuxDCHPInterfaces(file));
        }
      });
    } catch (e) {
      util.noop();
    }
    return result;
  }
  
  function getLinuxDHCPNics() {
    // alternate methods getting interfaces using DHCP
    let cmd = 'ip a 2> /dev/null';
    let result = [];
    try {
      const lines = execSync(cmd, { maxBuffer: 1024 * 20000 }).toString().split('\n');
      const nsections = splitSectionsNics(lines);
      result = (parseLinuxDHCPNics(nsections));
    } catch (e) {
      util.noop();
    }
    try {
      result = checkLinuxDCHPInterfaces('/etc/network/interfaces');
    } catch (e) {
      util.noop();
    }
    return result;
  }
  
  function parseLinuxDHCPNics(sections) {
    const result = [];
    if (sections && sections.length) {
      sections.forEach(lines => {
        if (lines && lines.length) {
          const parts = lines[0].split(':');
          if (parts.length > 2) {
            for (let line of lines) {
              if (line.indexOf(' inet ') >= 0 && line.indexOf(' dynamic ') >= 0) {
                const parts2 = line.split(' ');
                const nic = parts2[parts2.length - 1].trim();
                result.push(nic);
                break;
              }
            }
          }
        }
      });
    }
    return result;
  }
  
  function getLinuxIfaceDHCPstatus(iface, connectionName, DHCPNics) {
    let result = false;
    if (connectionName) {
      const cmd = `nmcli connection show "${connectionName}" 2>/dev/null \| grep ipv4.method;`;
      try {
        const lines = execSync(cmd).toString();
        const resultFormat = lines.replace(/\s+/g, ' ').trim();
  
        let dhcStatus = resultFormat.split(' ').slice(1).toString();
        switch (dhcStatus) {
          case 'auto':
            result = true;
            break;
  
          default:
            result = false;
            break;
        }
        return result;
      } catch (e) {
        return (DHCPNics.indexOf(iface) >= 0);
      }
    } else {
      return (DHCPNics.indexOf(iface) >= 0);
    }
  }
  
  function getDarwinIfaceDHCPstatus(iface) {
    let result = false;
    const cmd = `ipconfig getpacket "${iface}" 2>/dev/null \| grep lease_time;`;
    try {
      const lines = execSync(cmd).toString().split('\n');
      if (lines.length && lines[0].startsWith('lease_time')) {
        result = true;
      }
    } catch (e) {
      util.noop();
    }
    return result;
  }
  
  function getLinuxIfaceDNSsuffix(connectionName) {
    if (connectionName) {
      const cmd = `nmcli connection show "${connectionName}" 2>/dev/null \| grep ipv4.dns-search;`;
      try {
        const result = execSync(cmd).toString();
        const resultFormat = result.replace(/\s+/g, ' ').trim();
        const dnsSuffix = resultFormat.split(' ').slice(1).toString();
        return dnsSuffix == '--' ? 'Not defined' : dnsSuffix;
      } catch (e) {
        return 'Unknown';
      }
    } else {
      return 'Unknown';
    }
  }
  
  function getLinuxIfaceIEEE8021xAuth(connectionName) {
    if (connectionName) {
      const cmd = `nmcli connection show "${connectionName}" 2>/dev/null \| grep 802-1x.eap;`;
      try {
        const result = execSync(cmd).toString();
        const resultFormat = result.replace(/\s+/g, ' ').trim();
        const authenticationProtocol = resultFormat.split(' ').slice(1).toString();
  
  
        return authenticationProtocol == '--' ? '' : authenticationProtocol;
      } catch (e) {
        return 'Not defined';
      }
    } else {
      return 'Not defined';
    }
  }
  
  function getLinuxIfaceIEEE8021xState(authenticationProtocol) {
    if (authenticationProtocol) {
      if (authenticationProtocol == 'Not defined') {
        return 'Disabled';
      }
      return 'Enabled';
    } else {
      return 'Unknown';
    }
  }
  
  function testVirtualNic(iface, ifaceName, mac) {
    const virtualMacs = ['00:00:00:00:00:00', '00:03:FF', '00:05:69', '00:0C:29', '00:0F:4B', '00:0F:4B', '00:13:07', '00:13:BE', '00:15:5d', '00:16:3E', '00:1C:42', '00:21:F6', '00:21:F6', '00:24:0B', '00:24:0B', '00:50:56', '00:A0:B1', '00:E0:C8', '08:00:27', '0A:00:27', '18:92:2C', '16:DF:49', '3C:F3:92', '54:52:00', 'FC:15:97'];
    if (mac) {
      return virtualMacs.filter(item => { return mac.toUpperCase().toUpperCase().startsWith(item.substr(0, mac.length)); }).length > 0 ||
        iface.toLowerCase().indexOf(' virtual ') > -1 ||
        ifaceName.toLowerCase().indexOf(' virtual ') > -1 ||
        iface.toLowerCase().indexOf('vethernet ') > -1 ||
        ifaceName.toLowerCase().indexOf('vethernet ') > -1 ||
        iface.toLowerCase().startsWith('veth') ||
        ifaceName.toLowerCase().startsWith('veth') ||
        iface.toLowerCase().startsWith('vboxnet') ||
        ifaceName.toLowerCase().startsWith('vboxnet');
    } else return false;
  }
  
  function networkInterfaces(callback, rescan = true) {
  
    if (typeof callback === "boolean") {
      rescan = callback;
      callback = null;
    }
    return new Promise((resolve) => {
      process.nextTick(() => {
        let ifaces = os.networkInterfaces();
  
        let result = [];
        let nics = [];
        let dnsSuffixes = [];
        let nics8021xInfo = [];
        // seperate handling in OSX
        if (_darwin || _freebsd || _openbsd || _netbsd) {
          nics = getDarwinNics();
  
  
          nics.forEach(nic => {
  
            if ({}.hasOwnProperty.call(ifaces, nic.iface)) {
              ifaces[nic.iface].forEach(function (details) {
                if (details.family === 'IPv4') {
                  nic.ip4subnet = details.netmask;
                }
                if (details.family === 'IPv6') {
                  nic.ip6subnet = details.netmask;
                }
              });
            }
  
            result.push({
              iface: nic.iface,
              ifaceName: nic.iface,
              ip4: nic.ip4,
              ip4subnet: nic.ip4subnet || '',
              ip6: nic.ip6,
              ip6subnet: nic.ip6subnet || '',
              mac: nic.mac,
              internal: nic.internal,
              virtual: nic.internal ? false : testVirtualNic(nic.iface, nic.iface, nic.mac),
              operstate: nic.operstate,
              type: nic.type,
              duplex: nic.duplex,
              mtu: nic.mtu,
              speed: nic.speed,
              dhcp: getDarwinIfaceDHCPstatus(nic.iface),
              dnsSuffix: '',
              ieee8021xAuth: '',
              ieee8021xState: '',
              carrierChanges: 0
            });
          });
          _networkInterfaces = result;
          if (callback) { callback(result); }
          resolve(result);
        } else {
          if ((JSON.stringify(ifaces) === JSON.stringify(_ifaces)) && !rescan) {
            // no changes - just return object
            result = _networkInterfaces;
  
            if (callback) { callback(result); }
            resolve(result);
          } else {
            _ifaces = Object.assign({}, ifaces);
  
            if (_windows) {
              getWindowsNics().forEach(nic => {
                let found = false;
                Object.keys(ifaces).forEach(key => {
                  if (!found) {
                    ifaces[key].forEach(value => {
                      if (Object.keys(value).indexOf('mac') >= 0) {
                        found = value['mac'] === nic.mac;
                      }
                    });
                  }
                });
  
                if (!found) {
                  ifaces[nic.name] = [{ mac: nic.mac }];
                }
              });
  
              nics8021xInfo = getWindowsWiredProfilesInformation();
              nics = getWindowsNics();
              dnsSuffixes = getWindowsDNSsuffixes();
            }
            if (_linux) {
              _dhcpNics = getLinuxDHCPNics();
            }
            for (let dev in ifaces) {
              let ip4 = '';
              let ip4subnet = '';
              let ip6 = '';
              let ip6subnet = '';
              let mac = '';
              let duplex = '';
              let mtu = '';
              let speed = -1;
              let carrierChanges = 0;
              let operstate = 'down';
              let dhcp = false;
              let dnsSuffix = '';
              let ieee8021xAuth = '';
              let ieee8021xState = '';
              let type = '';
  
              if ({}.hasOwnProperty.call(ifaces, dev)) {
                let ifaceName = dev;
                ifaces[dev].forEach(function (details) {
                  if (details.family === 'IPv4') {
                    ip4 = details.address;
                    ip4subnet = details.netmask;
                  }
                  if (details.family === 'IPv6') {
                    if (!ip6 || ip6.match(/^fe80::/i)) {
                      ip6 = details.address;
                      ip6subnet = details.netmask;
                    }
                  }
                  mac = details.mac;
                  // fallback due to https://github.com/nodejs/node/issues/13581 (node 8.1 - node 8.2)
                  if (mac.indexOf('00:00:0') > -1 && (_linux || _darwin) && parseInt(process.versions.node.split('.'), 10) === 8) {
                    if (Object.keys(_mac).length === 0) {
                      _mac = getMacAddresses();
                    }
                    mac = _mac[dev] || '';
                  }
                });
                if (_linux) {
                  let iface = dev.split(':')[0].trim().toLowerCase();
                  const cmd = `echo -n "addr_assign_type: "; cat /sys/class/net/${iface}/addr_assign_type 2>/dev/null; echo;
              echo -n "address: "; cat /sys/class/net/${iface}/address 2>/dev/null; echo;
              echo -n "addr_len: "; cat /sys/class/net/${iface}/addr_len 2>/dev/null; echo;
              echo -n "broadcast: "; cat /sys/class/net/${iface}/broadcast 2>/dev/null; echo;
              echo -n "carrier: "; cat /sys/class/net/${iface}/carrier 2>/dev/null; echo;
              echo -n "carrier_changes: "; cat /sys/class/net/${iface}/carrier_changes 2>/dev/null; echo;
              echo -n "dev_id: "; cat /sys/class/net/${iface}/dev_id 2>/dev/null; echo;
              echo -n "dev_port: "; cat /sys/class/net/${iface}/dev_port 2>/dev/null; echo;
              echo -n "dormant: "; cat /sys/class/net/${iface}/dormant 2>/dev/null; echo;
              echo -n "duplex: "; cat /sys/class/net/${iface}/duplex 2>/dev/null; echo;
              echo -n "flags: "; cat /sys/class/net/${iface}/flags 2>/dev/null; echo;
              echo -n "gro_flush_timeout: "; cat /sys/class/net/${iface}/gro_flush_timeout 2>/dev/null; echo;
              echo -n "ifalias: "; cat /sys/class/net/${iface}/ifalias 2>/dev/null; echo;
              echo -n "ifindex: "; cat /sys/class/net/${iface}/ifindex 2>/dev/null; echo;
              echo -n "iflink: "; cat /sys/class/net/${iface}/iflink 2>/dev/null; echo;
              echo -n "link_mode: "; cat /sys/class/net/${iface}/link_mode 2>/dev/null; echo;
              echo -n "mtu: "; cat /sys/class/net/${iface}/mtu 2>/dev/null; echo;
              echo -n "netdev_group: "; cat /sys/class/net/${iface}/netdev_group 2>/dev/null; echo;
              echo -n "operstate: "; cat /sys/class/net/${iface}/operstate 2>/dev/null; echo;
              echo -n "proto_down: "; cat /sys/class/net/${iface}/proto_down 2>/dev/null; echo;
              echo -n "speed: "; cat /sys/class/net/${iface}/speed 2>/dev/null; echo;
              echo -n "tx_queue_len: "; cat /sys/class/net/${iface}/tx_queue_len 2>/dev/null; echo;
              echo -n "type: "; cat /sys/class/net/${iface}/type 2>/dev/null; echo;
              echo -n "wireless: "; cat /proc/net/wireless 2>/dev/null \| grep ${iface}; echo;
              echo -n "wirelessspeed: "; iw dev ${iface} link 2>&1 \| grep bitrate; echo;`;
  
                  let lines = [];
                  try {
                    lines = execSync(cmd).toString().split('\n');
                    const connectionName = getLinuxIfaceConnectionName(iface);
                    dhcp = getLinuxIfaceDHCPstatus(iface, connectionName, _dhcpNics);
                    dnsSuffix = getLinuxIfaceDNSsuffix(connectionName);
                    ieee8021xAuth = getLinuxIfaceIEEE8021xAuth(connectionName);
                    ieee8021xState = getLinuxIfaceIEEE8021xState(ieee8021xAuth);
                  } catch (e) {
                    util.noop();
                  }
                  duplex = util.getValue(lines, 'duplex');
                  duplex = duplex.startsWith('cat') ? '' : duplex;
                  mtu = parseInt(util.getValue(lines, 'mtu'), 10);
                  let myspeed = parseInt(util.getValue(lines, 'speed'), 10);
                  speed = isNaN(myspeed) ? -1 : myspeed;
                  let wirelessspeed = util.getValue(lines, 'wirelessspeed').split('tx bitrate: ');
                  if (speed === -1 && wirelessspeed.length === 2) {
                    myspeed = parseFloat(wirelessspeed[1]);
                    speed = isNaN(myspeed) ? -1 : myspeed;
                  }
                  carrierChanges = parseInt(util.getValue(lines, 'carrier_changes'), 10);
                  operstate = util.getValue(lines, 'operstate');
                  type = operstate === 'up' ? (util.getValue(lines, 'wireless').trim() ? 'wireless' : 'wired') : 'unknown';
                  if (iface === 'lo' || iface.startsWith('bond')) { type = 'virtual'; }
                }
                if (_windows) {
  
  
                  dnsSuffix = getWindowsIfaceDNSsuffix(dnsSuffixes.ifaces, dev);
                  nics.forEach(detail => {
                    if (detail.mac === mac) {
                      ifaceName = detail.name;
                      dhcp = detail.dhcp;
                      operstate = detail.operstate;
                      speed = detail.speed;
                      type = detail.type;
                    }
                  });
  
                  if (dev.toLowerCase().indexOf('wlan') >= 0 || ifaceName.toLowerCase().indexOf('wlan') >= 0 || ifaceName.toLowerCase().indexOf('802.11n') >= 0 || ifaceName.toLowerCase().indexOf('wireless') >= 0 || ifaceName.toLowerCase().indexOf('wi-fi') >= 0 || ifaceName.toLowerCase().indexOf('wifi') >= 0) {
                    type = 'wireless';
                  }
  
                  const IEEE8021x = getWindowsIEEE8021x(type, dev, nics8021xInfo);
                  ieee8021xAuth = IEEE8021x.protocol;
                  ieee8021xState = IEEE8021x.state;
                }
                let internal = (ifaces[dev] && ifaces[dev][0]) ? ifaces[dev][0].internal : null;
                const virtual = internal ? false : testVirtualNic(dev, ifaceName, mac);
                result.push({
                  iface: dev,
                  ifaceName,
                  ip4,
                  ip4subnet,
                  ip6,
                  ip6subnet,
                  mac,
                  internal,
                  virtual,
                  operstate,
                  type,
                  duplex,
                  mtu,
                  speed,
                  dhcp,
                  dnsSuffix,
                  ieee8021xAuth,
                  ieee8021xState,
                  carrierChanges,
                });
              }
            }
            _networkInterfaces = result;
            if (callback) { callback(result); }
            resolve(result);
          }
        }
      });
    });
  }
  
  exports.networkInterfaces = networkInterfaces;
  
  // --------------------------
  // NET - Speed
  
  function calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors) {
    let result = {
      iface,
      operstate,
      rx_bytes,
      rx_dropped,
      rx_errors,
      tx_bytes,
      tx_dropped,
      tx_errors,
      rx_sec: -1,
      tx_sec: -1,
      ms: 0
    };
  
    if (_network[iface] && _network[iface].ms) {
      result.ms = Date.now() - _network[iface].ms;
      result.rx_sec = (rx_bytes - _network[iface].rx_bytes) >= 0 ? (rx_bytes - _network[iface].rx_bytes) / (result.ms / 1000) : 0;
      result.tx_sec = (tx_bytes - _network[iface].tx_bytes) >= 0 ? (tx_bytes - _network[iface].tx_bytes) / (result.ms / 1000) : 0;
      _network[iface].rx_bytes = rx_bytes;
      _network[iface].tx_bytes = tx_bytes;
      _network[iface].rx_sec = result.rx_sec;
      _network[iface].tx_sec = result.tx_sec;
      _network[iface].ms = Date.now();
      _network[iface].last_ms = result.ms;
      _network[iface].operstate = operstate;
    } else {
      if (!_network[iface]) _network[iface] = {};
      _network[iface].rx_bytes = rx_bytes;
      _network[iface].tx_bytes = tx_bytes;
      _network[iface].rx_sec = -1;
      _network[iface].tx_sec = -1;
      _network[iface].ms = Date.now();
      _network[iface].last_ms = 0;
      _network[iface].operstate = operstate;
    }
    return result;
  }
  
  function networkStats(ifaces, callback) {
  
    let ifacesArray = [];
    // fallback - if only callback is given
    if (util.isFunction(ifaces) && !callback) {
      callback = ifaces;
      ifacesArray = [getDefaultNetworkInterface()];
    } else {
      ifaces = ifaces || getDefaultNetworkInterface();
      ifaces = ifaces.trim().toLowerCase().replace(/,+/g, '|');
      ifacesArray = ifaces.split('|');
    }
  
    return new Promise((resolve) => {
      process.nextTick(() => {
  
        const result = [];
  
        const workload = [];
        if (ifacesArray.length && ifacesArray[0].trim() === '*') {
          ifacesArray = [];
          networkInterfaces(false).then(allIFaces => {
            for (let iface of allIFaces) {
              ifacesArray.push(iface.iface);
            }
            networkStats(ifacesArray.join(',')).then(result => {
              if (callback) { callback(result); }
              resolve(result);
            });
          });
        } else {
          for (let iface of ifacesArray) {
            workload.push(networkStatsSingle(iface.trim()));
          }
          if (workload.length) {
            Promise.all(
              workload
            ).then(data => {
              if (callback) { callback(data); }
              resolve(data);
            });
          } else {
            if (callback) { callback(result); }
            resolve(result);
          }
        }
      });
    });
  }
  
  function networkStatsSingle(iface) {
  
    function parseLinesWindowsPerfData(sections) {
      let perfData = [];
      for (let i in sections) {
        if ({}.hasOwnProperty.call(sections, i)) {
          if (sections[i].trim() !== '') {
            let lines = sections[i].trim().split('\r\n');
            perfData.push({
              name: util.getValue(lines, 'Name', '=').replace(/[()\[\] ]+/g, '').replace('#', '_').toLowerCase(),
              rx_bytes: parseInt(util.getValue(lines, 'BytesReceivedPersec', '='), 10),
              rx_errors: parseInt(util.getValue(lines, 'PacketsReceivedErrors', '='), 10),
              rx_dropped: parseInt(util.getValue(lines, 'PacketsReceivedDiscarded', '='), 10),
              tx_bytes: parseInt(util.getValue(lines, 'BytesSentPersec', '='), 10),
              tx_errors: parseInt(util.getValue(lines, 'PacketsOutboundErrors', '='), 10),
              tx_dropped: parseInt(util.getValue(lines, 'PacketsOutboundDiscarded', '='), 10)
            });
          }
        }
      }
      return perfData;
    }
  
    return new Promise((resolve) => {
      process.nextTick(() => {
  
        const ifaceSanitized = util.sanitizeShellString(iface);
  
        let result = {
          iface: ifaceSanitized,
          operstate: 'unknown',
          rx_bytes: 0,
          rx_dropped: 0,
          rx_errors: 0,
          tx_bytes: 0,
          tx_dropped: 0,
          tx_errors: 0,
          rx_sec: -1,
          tx_sec: -1,
          ms: 0
        };
  
        let operstate = 'unknown';
        let rx_bytes = 0;
        let tx_bytes = 0;
        let rx_dropped = 0;
        let rx_errors = 0;
        let tx_dropped = 0;
        let tx_errors = 0;
  
        let cmd, lines, stats;
        if (!_network[ifaceSanitized] || (_network[ifaceSanitized] && !_network[ifaceSanitized].ms) || (_network[ifaceSanitized] && _network[ifaceSanitized].ms && Date.now() - _network[ifaceSanitized].ms >= 500)) {
          if (_linux) {
            if (fs.existsSync('/sys/class/net/' + ifaceSanitized)) {
              cmd =
                'cat /sys/class/net/' + ifaceSanitized + '/operstate; ' +
                'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_bytes; ' +
                'cat /sys/class/net/' + ifaceSanitized + '/statistics/tx_bytes; ' +
                'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_dropped; ' +
                'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_errors; ' +
                'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_dropped; ' +
                'cat /sys/class/net/' + ifaceSanitized + '/statistics/tx_errors; ';
              exec(cmd, function (error, stdout) {
                if (!error) {
                  lines = stdout.toString().split('\n');
                  operstate = lines[0].trim();
                  rx_bytes = parseInt(lines[1], 10);
                  tx_bytes = parseInt(lines[2], 10);
                  rx_dropped = parseInt(lines[3], 10);
                  rx_errors = parseInt(lines[4], 10);
                  tx_dropped = parseInt(lines[5], 10);
                  tx_errors = parseInt(lines[6], 10);
  
                  result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
  
                }
                resolve(result);
              });
            } else {
              resolve(result);
            }
          }
          if (_freebsd || _openbsd || _netbsd) {
            cmd = 'netstat -ibndI ' + ifaceSanitized;
            exec(cmd, function (error, stdout) {
              if (!error) {
                lines = stdout.toString().split('\n');
                for (let i = 1; i < lines.length; i++) {
                  const line = lines[i].replace(/ +/g, ' ').split(' ');
                  if (line && line[0] && line[7] && line[10]) {
                    rx_bytes = rx_bytes + parseInt(line[7]);
                    if (line[6].trim() !== '-') { rx_dropped = rx_dropped + parseInt(line[6]); }
                    if (line[5].trim() !== '-') { rx_errors = rx_errors + parseInt(line[5]); }
                    tx_bytes = tx_bytes + parseInt(line[10]);
                    if (line[12].trim() !== '-') { tx_dropped = tx_dropped + parseInt(line[12]); }
                    if (line[9].trim() !== '-') { tx_errors = tx_errors + parseInt(line[9]); }
                    operstate = 'up';
                  }
                }
                result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
              }
              resolve(result);
            });
          }
          if (_darwin) {
            cmd = 'ifconfig ' + ifaceSanitized + ' | grep "status"';
            exec(cmd, function (error, stdout) {
              result.operstate = (stdout.toString().split(':')[1] || '').trim();
              result.operstate = (result.operstate || '').toLowerCase();
              result.operstate = (result.operstate === 'active' ? 'up' : (result.operstate === 'inactive' ? 'down' : 'unknown'));
              cmd = 'netstat -bdI ' + ifaceSanitized;
              exec(cmd, function (error, stdout) {
                if (!error) {
                  lines = stdout.toString().split('\n');
                  // if there is less than 2 lines, no information for this interface was found
                  if (lines.length > 1 && lines[1].trim() !== '') {
                    // skip header line
                    // use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address
                    stats = lines[1].replace(/ +/g, ' ').split(' ');
                    rx_bytes = parseInt(stats[6]);
                    rx_dropped = parseInt(stats[11]);
                    rx_errors = parseInt(stats[5]);
                    tx_bytes = parseInt(stats[9]);
                    tx_dropped = parseInt(stats[11]);
                    tx_errors = parseInt(stats[8]);
  
                    result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, result.operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
                  }
                }
                resolve(result);
              });
            });
          }
          if (_windows) {
            let perfData = [];
            let ifaceName = ifaceSanitized;
  
            // Performance Data
            util.wmic('path Win32_PerfRawData_Tcpip_NetworkInterface Get name,BytesReceivedPersec,BytesSentPersec,BytesTotalPersec,PacketsOutboundDiscarded,PacketsOutboundErrors,PacketsReceivedDiscarded,PacketsReceivedErrors /value').then((stdout, error) => {
              if (!error) {
                const psections = stdout.toString().split(/\n\s*\n/);
                perfData = parseLinesWindowsPerfData(psections);
              }
  
              // Network Interfaces
              networkInterfaces(false).then(interfaces => {
                // get bytes sent, received from perfData by name
                rx_bytes = 0;
                tx_bytes = 0;
                perfData.forEach(detail => {
                  interfaces.forEach(det => {
                    if ((det.iface.toLowerCase() === ifaceSanitized.toLowerCase() ||
                      det.mac.toLowerCase() === ifaceSanitized.toLowerCase() ||
                      det.ip4.toLowerCase() === ifaceSanitized.toLowerCase() ||
                      det.ip6.toLowerCase() === ifaceSanitized.toLowerCase() ||
                      det.ifaceName.replace(/[()\[\] ]+/g, '').replace('#', '_').toLowerCase() === ifaceSanitized.replace(/[()\[\] ]+/g, '').replace('#', '_').toLowerCase()) &&
                      (det.ifaceName.replace(/[()\[\] ]+/g, '').replace('#', '_').toLowerCase() === detail.name)) {
                      ifaceName = det.iface;
                      rx_bytes = detail.rx_bytes;
                      rx_dropped = detail.rx_dropped;
                      rx_errors = detail.rx_errors;
                      tx_bytes = detail.tx_bytes;
                      tx_dropped = detail.tx_dropped;
                      tx_errors = detail.tx_errors;
                      operstate = det.operstate;
                    }
                  });
                });
                if (rx_bytes && tx_bytes) {
                  result = calcNetworkSpeed(ifaceName, parseInt(rx_bytes), parseInt(tx_bytes), operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
                }
                resolve(result);
              });
            });
          }
        } else {
          result.rx_bytes = _network[ifaceSanitized].rx_bytes;
          result.tx_bytes = _network[ifaceSanitized].tx_bytes;
          result.rx_sec = _network[ifaceSanitized].rx_sec;
          result.tx_sec = _network[ifaceSanitized].tx_sec;
          result.ms = _network[ifaceSanitized].last_ms;
          result.operstate = _network[ifaceSanitized].operstate;
          resolve(result);
        }
      });
    });
  }
  
  exports.networkStats = networkStats;
  
  // --------------------------
  // NET - connections (sockets)
  
  function networkConnections(callback) {
  
    return new Promise((resolve) => {
      process.nextTick(() => {
        let result = [];
        if (_linux || _freebsd || _openbsd || _netbsd) {
          let cmd = 'export LC_ALL=C; netstat -tunap | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"; unset LC_ALL';
          if (_freebsd || _openbsd || _netbsd) cmd = 'export LC_ALL=C; netstat -na | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"; unset LC_ALL';
          exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
            let lines = stdout.toString().split('\n');
            if (!error && (lines.length > 1 || lines[0] != '')) {
              lines.forEach(function (line) {
                line = line.replace(/ +/g, ' ').split(' ');
                if (line.length >= 7) {
                  let localip = line[3];
                  let localport = '';
                  let localaddress = line[3].split(':');
                  if (localaddress.length > 1) {
                    localport = localaddress[localaddress.length - 1];
                    localaddress.pop();
                    localip = localaddress.join(':');
                  }
                  let peerip = line[4];
                  let peerport = '';
                  let peeraddress = line[4].split(':');
                  if (peeraddress.length > 1) {
                    peerport = peeraddress[peeraddress.length - 1];
                    peeraddress.pop();
                    peerip = peeraddress.join(':');
                  }
                  let connstate = line[5];
                  // if (connstate === 'VERBUNDEN') connstate = 'ESTABLISHED';
                  let proc = line[6].split('/');
  
                  if (connstate) {
                    result.push({
                      protocol: line[0],
                      localaddress: localip,
                      localport: localport,
                      peeraddress: peerip,
                      peerport: peerport,
                      state: connstate,
                      pid: proc[0] && proc[0] !== '-' ? parseInt(proc[0], 10) : -1,
                      process: proc[1] ? proc[1].split(' ')[0] : ''
                    });
                  }
                }
              });
              if (callback) {
                callback(result);
              }
              resolve(result);
            } else {
              cmd = 'ss -tunap | grep "ESTAB\\|SYN-SENT\\|SYN-RECV\\|FIN-WAIT1\\|FIN-WAIT2\\|TIME-WAIT\\|CLOSE\\|CLOSE-WAIT\\|LAST-ACK\\|LISTEN\\|CLOSING"';
              exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
  
                if (!error) {
                  let lines = stdout.toString().split('\n');
                  lines.forEach(function (line) {
                    line = line.replace(/ +/g, ' ').split(' ');
                    if (line.length >= 6) {
                      let localip = line[4];
                      let localport = '';
                      let localaddress = line[4].split(':');
                      if (localaddress.length > 1) {
                        localport = localaddress[localaddress.length - 1];
                        localaddress.pop();
                        localip = localaddress.join(':');
                      }
                      let peerip = line[5];
                      let peerport = '';
                      let peeraddress = line[5].split(':');
                      if (peeraddress.length > 1) {
                        peerport = peeraddress[peeraddress.length - 1];
                        peeraddress.pop();
                        peerip = peeraddress.join(':');
                      }
                      let connstate = line[1];
                      if (connstate === 'ESTAB') connstate = 'ESTABLISHED';
                      if (connstate === 'TIME-WAIT') connstate = 'TIME_WAIT';
                      let pid = -1;
                      let process = '';
                      if (line.length >= 7 && line[6].indexOf('users:') > -1) {
                        let proc = line[6].replace('users:(("', '').replace(/"/g, '').split(',');
                        if (proc.length > 2) {
                          process = proc[0].split(' ')[0];
                          pid = parseInt(proc[1], 10);
                        }
                      }
                      if (connstate) {
                        result.push({
                          protocol: line[0],
                          localaddress: localip,
                          localport: localport,
                          peeraddress: peerip,
                          peerport: peerport,
                          state: connstate,
                          pid,
                          process
                        });
                      }
                    }
                  });
                }
                if (callback) {
                  callback(result);
                }
                resolve(result);
              });
            }
          });
        }
        if (_darwin) {
          let cmd = 'netstat -natv | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"';
          exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
            if (!error) {
  
              let lines = stdout.toString().split('\n');
  
              lines.forEach(function (line) {
                line = line.replace(/ +/g, ' ').split(' ');
                if (line.length >= 8) {
                  let localip = line[3];
                  let localport = '';
                  let localaddress = line[3].split('.');
                  if (localaddress.length > 1) {
                    localport = localaddress[localaddress.length - 1];
                    localaddress.pop();
                    localip = localaddress.join('.');
                  }
                  let peerip = line[4];
                  let peerport = '';
                  let peeraddress = line[4].split('.');
                  if (peeraddress.length > 1) {
                    peerport = peeraddress[peeraddress.length - 1];
                    peeraddress.pop();
                    peerip = peeraddress.join('.');
                  }
                  let connstate = line[5];
                  let pid = parseInt(line[8], 10);
                  if (connstate) {
                    result.push({
                      protocol: line[0],
                      localaddress: localip,
                      localport: localport,
                      peeraddress: peerip,
                      peerport: peerport,
                      state: connstate,
                      pid: pid,
                      process: ''
                    });
                  }
                }
              });
              if (callback) {
                callback(result);
              }
              resolve(result);
            }
          });
        }
        if (_windows) {
          let cmd = 'netstat -nao';
          try {
            exec(cmd, util.execOptsWin, function (error, stdout) {
              if (!error) {
  
                let lines = stdout.toString().split('\r\n');
  
                lines.forEach(function (line) {
                  line = line.trim().replace(/ +/g, ' ').split(' ');
                  if (line.length >= 4) {
                    let localip = line[1];
                    let localport = '';
                    let localaddress = line[1].split(':');
                    if (localaddress.length > 1) {
                      localport = localaddress[localaddress.length - 1];
                      localaddress.pop();
                      localip = localaddress.join(':');
                    }
                    let peerip = line[2];
                    let peerport = '';
                    let peeraddress = line[2].split(':');
                    if (peeraddress.length > 1) {
                      peerport = peeraddress[peeraddress.length - 1];
                      peeraddress.pop();
                      peerip = peeraddress.join(':');
                    }
                    let pid = line[4];
                    let connstate = line[3];
                    if (connstate === 'HERGESTELLT') connstate = 'ESTABLISHED';
                    if (connstate.startsWith('ABH')) connstate = 'LISTEN';
                    if (connstate === 'SCHLIESSEN_WARTEN') connstate = 'CLOSE_WAIT';
                    if (connstate === 'WARTEND') connstate = 'TIME_WAIT';
                    if (connstate === 'SYN_GESENDET') connstate = 'SYN_SENT';
  
                    if (connstate === 'LISTENING') connstate = 'LISTEN';
                    if (connstate === 'SYN_RECEIVED') connstate = 'SYN_RECV';
                    if (connstate === 'FIN_WAIT_1') connstate = 'FIN_WAIT1';
                    if (connstate === 'FIN_WAIT_2') connstate = 'FIN_WAIT2';
                    if (connstate) {
                      result.push({
                        protocol: line[0].toLowerCase(),
                        localaddress: localip,
                        localport: localport,
                        peeraddress: peerip,
                        peerport: peerport,
                        state: connstate,
                        pid,
                        process: ''
                      });
                    }
                  }
                });
                if (callback) {
                  callback(result);
                }
                resolve(result);
              }
            });
          } catch (e) {
            if (callback) { callback(result); }
            resolve(result);
          }
        }
      });
    });
  }
  
  exports.networkConnections = networkConnections;
  
  function networkGatewayDefault(callback) {
  
    return new Promise((resolve) => {
      process.nextTick(() => {
        let result = '';
        if (_linux || _freebsd || _openbsd || _netbsd) {
          let cmd = 'ip route get 1';
          try {
            exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
              if (!error) {
                let lines = stdout.toString().split('\n');
                const line = lines && lines[0] ? lines[0] : '';
                let parts = line.split(' via ');
                if (parts && parts[1]) {
                  parts = parts[1].split(' ');
                  result = parts[0];
                }
                if (callback) {
                  callback(result);
                }
                resolve(result);
              } else {
                if (callback) {
                  callback(result);
                }
                resolve(result);
              }
            });
          } catch (e) {
            if (callback) { callback(result); }
            resolve(result);
          }
        }
        if (_darwin) {
          let cmd = 'route -n get default';
          try {
            exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
              if (!error) {
                let lines = stdout.toString().split('\n').map(line => line.trim());
                result = util.getValue(lines, 'gateway');
                if (callback) {
                  callback(result);
                }
                resolve(result);
              } else {
                if (callback) {
                  callback(result);
                }
                resolve(result);
              }
            });
          } catch (e) {
            if (callback) { callback(result); }
            resolve(result);
          }
        }
        if (_windows) {
          try {
            exec('netstat -r', util.execOptsWin, function (error, stdout) {
              const lines = stdout.toString().split(os.EOL);
              lines.forEach(line => {
                line = line.replace(/\s+/g, ' ').trim();
                if (line.indexOf('0.0.0.0 0.0.0.0') > -1 && !(/[a-zA-Z]/.test(line))) {
                  const parts = line.split(' ');
                  if (parts.length >= 5 && (parts[parts.length - 3]).indexOf('.') > -1) {
                    result = parts[parts.length - 3];
                  }
                }
              });
              if (!result) {
                util.powerShell('Get-CimInstance -ClassName Win32_IP4RouteTable | Where-Object { $_.Destination -eq \'0.0.0.0\' -and $_.Mask -eq \'0.0.0.0\' }')
                  .then(data => {
                    let lines = data.toString().split('\r\n');
                    if (lines.length > 1 && !result) {
                      result = util.getValue(lines, 'NextHop');
                      if (callback) {
                        callback(result);
                      }
                      resolve(result);
                      // } else {
                      //   exec('ipconfig', util.execOptsWin, function (error, stdout) {
                      //     let lines = stdout.toString().split('\r\n');
                      //     lines.forEach(function (line) {
                      //       line = line.trim().replace(/\. /g, '');
                      //       line = line.trim().replace(/ +/g, '');
                      //       const parts = line.split(':');
                      //       if ((parts[0].toLowerCase().startsWith('standardgate') || parts[0].toLowerCase().indexOf('gateway') > -1 || parts[0].toLowerCase().indexOf('enlace') > -1) && parts[1]) {
                      //         result = parts[1];
                      //       }
                      //     });
                      //     if (callback) { callback(result); }
                      //     resolve(result);
                      //   });
                    }
                  });
              } else {
                if (callback) {
                  callback(result);
                }
                resolve(result);
              }
            });
          } catch (e) {
            if (callback) { callback(result); }
            resolve(result);
          }
        }
      });
    });
  }
  
  exports.networkGatewayDefault = networkGatewayDefault;
  