CVE-2024-21488: Improper Neutralization of Special Elements used in a Command ('Command Injection') (CWE-77)
value: Versions of the package network before 0.7.0 are vulnerable to Arbitrary Command Injection due to use of the child_process exec function without input sanitization. If (attacker-controlled) user input is given to the mac_address_for function of the package, it is possible for the attacker to execute arbitrary commands on the operating system that this package is being run on.
CWE description: The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
CVSS v3 base score: 9.8
Published: 2024-01-30T05:15Z
Repository: https://github.com/tomas/network
Language: JavaScript
Files changed: .gitignore, README.md, bin/network, lib/darwin.js, lib/index.js, lib/linux.js, lib/win32.js, package.json, test.js
Commit date: 2024-01-23 22:32:17 -0300
Commit message: Ensure valid nic in darwin too
Diff: diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c600d12
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*~
+.DS_Store
+node_modules
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d1bcd4a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,113 @@
+Network Utilies for Node.js
+===========================
+
+# Install
+
+    $ npm install network
+
+# Usage
+
+## Get public IP
+
+Returns your public IP address, as reported by DynDNS.org or other services.
+
+``` js
+var network = require('network');
+
+network.get_public_ip(function(err, ip) {
+  console.log(err || ip); // should return your public IP address
+})
+```
+
+##### CLI
+
+    $ network public_ip
+
+## Get private IP
+
+Returns the IP address assigned to your first active network inteface.
+
+``` js
+network.get_private_ip(function(err, ip) {
+  console.log(err || ip); // err may be 'No active network interface found'.
+})
+```
+
+##### CLI
+
+    $ network private_ip
+
+## Get gateway IP
+
+Returns the IP of the gateway that your active network interface is linked to.
+
+``` js
+network.get_gateway_ip(function(err, ip) {
+  console.log(err || ip); // err may be 'No active network interface found.'
+})
+```
+
+##### CLI
+
+    $ network gateway_ip
+
+## Get active interface
+
+Returns the IP, MAC address and interface type for the active network
+interface. On OS X and Linux you also get the IP of its assigned gateway.
+
+``` js
+network.get_active_interface(function(err, obj) {
+
+  /* obj should be:
+
+  { name: 'eth0',
+    ip_address: '10.0.1.3',
+    mac_address: '56:e5:f9:e4:38:1d',
+    type: 'Wired',
+    netmask: '255.255.255.0',
+    gateway_ip: '10.0.1.1' }
+
+  */
+})
+```
+
+##### CLI
+
+    $ network active_interface
+
+## Get interfaces list
+
+Returns list of network interfaces, including MAC addresses and the such, just
+as in the example above.
+
+``` js
+network.get_interfaces_list(function(err, list) {
+
+  /* list should be:
+
+  [{
+    name: 'eth0',
+    ip_address: '10.0.1.3',
+    mac_address: '56:e5:f9:e4:38:1d',
+    type: 'Wired',
+    netmask: '255.255.255.0',
+    gateway_ip: '10.0.1.1' 
+   },
+   { ... }, { ... }]
+
+  */
+})
+```
+
+##### CLI
+
+    $ network interfaces_list
+
+# Copyright
+
+Written by Tomás Pollak. Copyright (c) Fork, Ltd.
+
+# License
+
+MIT.
diff --git a/bin/network b/bin/network
new file mode 100755
index 0000000..2e036a8
--- /dev/null
+++ b/bin/network
@@ -0,0 +1,148 @@
+#!/usr/bin/env node
+
+var program = require('commander'),
+    network = require('..'),
+    pkg     = require('../package.json');
+
+function print(results, json) {
+  if (json) console.log(JSON.stringify(results, null, 2))
+  else console.log(results)
+}
+
+program.version(pkg.version)
+
+program
+  .command('public_ip')
+  .description('Print public ip address')
+  .option('-j --json', 'Print results in JSON format')
+  .on('--help', function () {
+    console.log('  Examples:')
+    console.log()
+    console.log('    $ network public_ip')
+    console.log()
+  })
+  .action(function () {
+    network.get_public_ip(function (err, results) {
+      if (err) {
+        console.error(err)
+        process.exit(1)
+      }
+
+      print(results, program.json)
+      process.exit(0)
+    })
+  })
+
+program
+  .command('private_ip')
+  .description('Print private ip address')
+  .option('-j --json', 'Print results in JSON format')
+  .on('--help', function () {
+    console.log('  Examples:')
+    console.log()
+    console.log('    $ network private_ip')
+    console.log()
+  })
+  .action(function () {
+    network.get_private_ip(function (err, results) {
+      if (err) {
+        console.error(err)
+        process.exit(1)
+      }
+
+      print(results, program.json)
+      process.exit(0)
+    })
+  })
+
+program
+  .command('mac_address_for <interface>')
+  .description('Print mac address for given network interface')
+  .option('-j --json', 'Print results in JSON format')
+  .on('--help', function () {
+    console.log('  Examples:')
+    console.log()
+    console.log('    $ network mac_address_for Wi-Fi')
+    console.log()
+  })
+  .action(function (nInterface) {
+    network.mac_address_for(nInterface, function (err, results) {
+      if (err) {
+        console.error(err)
+        process.exit(1)
+      }
+
+      print(results, program.json)
+      process.exit(0)
+    })
+  })
+
+program
+  .command('gateway_ip')
+  .description('Print gateway ip address')
+  .option('-j --json', 'Print results in JSON format')
+  .on('--help', function () {
+    console.log('  Examples:')
+    console.log()
+    console.log('    $ network gateway_ip')
+    console.log()
+  })
+  .action(function () {
+    network.get_gateway_ip(function (err, results) {
+      if (err) {
+        console.error(err)
+        process.exit(1)
+      }
+
+      print(results, program.json)
+      process.exit(0)
+    })
+  })
+
+program
+  .command('active_interface')
+  .description('Print active network interface')
+  .option('-j --json', 'Print results in JSON format')
+  .on('--help', function () {
+    console.log('  Examples:')
+    console.log()
+    console.log('    $ network active_interface')
+    console.log()
+  })
+  .action(function () {
+    network.get_active_interface(function (err, results) {
+      if (err) {
+        console.error(err)
+        process.exit(1)
+      }
+
+      print(results, program.json)
+      process.exit(0)
+    })
+  })
+
+program
+  .command('interfaces_list')
+  .description('Print all network interfaces')
+  .option('-j --json', 'Print results in JSON format')
+  .on('--help', function () {
+    console.log('  Examples:')
+    console.log()
+    console.log('    $ network interfaces_list')
+    console.log()
+  })
+  .action(function () {
+    network.get_interfaces_list(function (err, results) {
+      if (err) {
+        console.error(err)
+        process.exit(1)
+      }
+
+      print(results, program.json)
+      process.exit(0)
+    })
+  })
+
+program.parse(process.argv)
+
+if (!program.args.length) program.help()
diff --git a/lib/darwin.js b/lib/darwin.js
new file mode 100644
index 0000000..2609650
--- /dev/null
+++ b/lib/darwin.js
@@ -0,0 +1,154 @@
+"use strict";
+
+var os    = require('os'),
+    exec  = require('child_process').exec,
+    async = require('async');
+
+//////////////////////////////////////////
+// helpers
+
+function trim_exec(cmd, cb) {
+  exec(cmd, function(err, out) {
+    if (out && out.toString() != '')
+      cb(null, out.toString().trim())
+    else
+      cb(err)
+  })
+}
+
+function ensure_valid_nic(str) {
+  if (str.match(/[^\w]/))
+    throw new Error("Invalid nic name given: " + str);
+}
+
+function determine_nic_type(str) {
+  return str.match(/Ethernet/)
+    ? "Wired"
+    : str.match(/Wi-?Fi|AirPort/i)
+    ? "Wireless"
+    : str.match(/FireWire/)
+    ? "FireWire"
+    : str.match(/Thunderbolt/)
+    ? "Thunderbolt"
+    : str.match(/Bluetooth/)
+    ? "Bluetooth"
+    : str.match(/USB 10\/100\/1000 LAN/)
+    ? "USB Ethernet Adapter"
+    : "Other";
+}
+
+//////////////////////////////////////////
+// exports
+
+exports.get_active_network_interface_name = function(cb) {
+  var cmd = "netstat -rn | grep UG | awk '{print $NF}'";
+  exec(cmd, function(err, stdout) {
+    if (err) return cb(err);
+
+    var raw = stdout.toString().trim().split('\n');
+    if (raw.length === 0 || raw === [''])
+      return cb(new Error('No active network interface found.'));
+
+    cb(null, raw[0]);
+  });
+};
+
+/* unused
+
+exports.interface_type_for = function(nic_name, cb) {
+  exec('networksetup -listnetworkserviceorder | grep ' + nic_name, function(err, out) {
+    if (err) return cb(err);
+
+    var type = out.toString().match(/ethernet|lan/i) ? 'Wired' : 'Wireless';
+    cb(null, type);
+  })
+};
+*/
+
+exports.mac_address_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  var cmd = "networksetup -getmacaddress " + nic_name + " | awk '{print $3}'";
+  trim_exec(cmd, cb);
+};
+
+exports.gateway_ip_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  var cmd = "ipconfig getoption " + nic_name + " router";
+  trim_exec(cmd, cb);
+};
+
+exports.netmask_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  var cmd = "ipconfig getoption " + nic_name + " subnet_mask";
+  trim_exec(cmd, cb);
+};
+exports.status_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  var cmd = "netstat -rn | grep " + nic_name + ' | grep UG | wc -l | sed -e "s/1/active/" | sed -e "s/0/inactive/"'
+  trim_exec(cmd, cb)
+}
+
+exports.get_network_interfaces_list = function(cb) {
+
+  var count = 0,
+      list  = [],
+      nics  = os.networkInterfaces();
+
+  function append_data(obj) {
+    async.parallel([
+      function(cb) {
+        exports.gateway_ip_for(obj.name, cb)
+      },
+      function(cb) {
+        exports.netmask_for(obj.name, cb)
+      },
+      function(cb){
+        exports.status_for(obj.name, cb)
+      }
+    ], function(err, results) {
+      if (results[0]) obj.gateway_ip = results[0];
+      if (results[1]) obj.netmask    = results[1];
+      if (results[2]) obj.status = results[2]
+      
+      list.push(obj);
+      --count || cb(null, list);
+    })
+  }
+
+  exec('networksetup -listallhardwareports', function(err, out) {
+    if (err) return cb(err);
+
+    var blocks = out.toString().split(/Hardware/).slice(1);
+    count = blocks.length;
+
+    blocks.forEach(function(block) {
+      var parts = block.match(/Port: (.+)/),
+          mac   = block.match(/Address: ([A-Fa-f0-9:-]+)/),
+          name  = block.match(/Device: (\w+)/);
+          
+
+      if (!parts || !mac || !name) 
+        return --count;
+
+      var obj   = {},
+          port  = parts[1];
+
+      obj.name  = name[1];
+      obj.desc  = port;
+      obj.type  = determine_nic_type(port);
+      obj.ip_address  = null;
+      obj.mac_address = mac[1];
+
+      (nics[obj.name] || []).forEach(function(type) {
+        if (type.family == 'IPv4') {
+          obj.ip_address = type.address;
+        }
+      });
+
+      append_data(obj);
+    })
+
+    if (count == 0) cb(new Error('No interfaces found.'))
+  })
+
+};
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..2751024
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,128 @@
+"use strict";
+
+var needle = require('needle');
+var os_functions;
+switch (process.platform) {
+  case 'win32':
+    os_functions = require('./win32');
+    break;
+  case 'linux':
+    os_functions = require('./linux');
+    break;
+  case 'darwin':
+    os_functions = require('./darwin');
+    break;
+}
+// var ip_regex = /((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})/;
+var ip_regex = /(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)/;
+
+var public_ip_urls = [
+  'http://checkip.dyndns.org',
+  'http://wtfismyip.com/text',
+  'http://ipecho.net/plain',
+  'http://ifconfig.me/ip'
+];
+
+function is_ip_address(str) {
+  return ip_regex.test(str);
+};
+
+function nic_by_name(name, cb) {
+  os_functions.get_network_interfaces_list(function(err, list) {
+    if (err) return cb(err);
+
+    var nics = list.filter(function(nic) { return nic.name === name; });
+
+    if (nics.length > 0)
+      cb(null, nics[0]);
+    else
+      cb(new Error('No network interface named ' + name));
+  });
+};
+
+exports.get_public_ip = function(options, cb) {
+
+  if (typeof options == 'function') { // no options passed
+    cb = options;
+    options = {};
+  }
+
+  var urls = options.urls || public_ip_urls;
+
+  function get(i) {
+    var url = urls[i];
+    if (!url) return cb(new Error('Unable to fetch IP address.'));
+
+    needle.get(url, function(err, resp) {
+      var body = resp && resp.body.toString();
+      if (body && body.match(ip_regex)) {
+        return cb(null, body.match(ip_regex)[1]);
+      }
+
+      get(i+1);
+    })
+  };
+
+  get(0);
+}
+
+exports.get_private_ip = function(cb) {
+  os_functions.get_network_interfaces_list(function(err, list) {
+    if (err || !list)
+      return cb(err || new Error('No network interfaces found.'));
+
+    os_functions.get_active_network_interface_name(function(err, active_nic) {
+      if (err) return cb(err);
+
+      var ips = list.filter(function(nic) {
+        if (is_ip_address(nic.ip_address))
+          return active_nic ? active_nic == nic.name : true;
+      });
+
+      if (ips.length > 0)
+        cb(null, ips[0].ip_address);
+      else
+        cb(new Error('No private IPs found (' + list.length + ' interfaces)'));
+    });
+  });
+};
+
+exports.get_gateway_ip = function(cb) {
+  os_functions.get_active_network_interface_name(function(err, nic_name) {
+    if (err || nic_name.trim() == '')
+      return cb(err || new Error('No active network interface found.'));
+
+    os_functions.gateway_ip_for(nic_name, function(err, out) {
+      if (err || !out || out.toString() == '')
+        return cb(err || new Error('No gateway IP assigned to ' + nic_name));
+
+      cb(null, out.toString().trim())
+    })
+  });
+};
+
+exports.get_active_interface = function(cb) {
+  os_functions.get_active_network_interface_name(function(err, nic_name) {
+    if (err || !nic_name) return cb(err || new Error("No active interfaces detected."));
+
+    nic_by_name(nic_name, function(err, nic) {
+      if (err) return cb(err);
+
+      os_functions.netmask_for(nic_name, function(err, netmask) {
+        if (!err && netmask)
+          nic.netmask = netmask.trim();
+
+        os_functions.gateway_ip_for(nic_name, function(err, ip) {
+          if (!err && ip)
+            nic.gateway_ip = ip.toString().trim();
+
+          cb(null, nic);
+        })
+      });
+    });
+  });
+};
+
+exports.get_interfaces_list = os_functions.get_network_interfaces_list;
+exports.mac_address_for = os_functions.mac_address_for;
+exports.gateway_ip_for = os_functions.gateway_ip_for;
diff --git a/lib/linux.js b/lib/linux.js
new file mode 100644
index 0000000..08de844
--- /dev/null
+++ b/lib/linux.js
@@ -0,0 +1,113 @@
+"use strict";
+
+var os    = require('os'),
+    exec  = require('child_process').exec,
+    async = require('async');
+
+function trim_exec(cmd, cb) {
+  exec(cmd, function(err, out) {
+    if (out && out.toString() != '')
+      cb(null, out.toString().trim())
+    else
+      cb(err)
+  })
+}
+
+function ensure_valid_nic(str) {
+  if (str.match(/[^\w]/))
+    throw new Error("Invalid nic name given: " + str);
+}
+
+// If no wifi, then there is no error but cbed get's a null in second param.
+exports.get_active_network_interface_name = function(cb) {
+  var cmd = "netstat -rn | grep UG | awk '{print $NF}'";
+  exec(cmd, function(err, stdout, stderr) {
+    if (err) return cb(err);
+
+    if (stderr.toString().match('not found')) {
+      return cb(new Error('Command failed: ' + stderr.toString().trim()))
+    }
+
+    var raw = stdout.toString().trim().split('\n');
+    if (raw.length === 0 || raw === [''])
+      return cb(new Error('No active network interface found.'));
+
+    cb(null, raw[0]);
+  });
+};
+
+exports.interface_type_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  exec('cat /proc/net/wireless | grep ' + nic_name, function(err, out) {
+    return cb(null, err ? 'Wired' : 'Wireless')
+  })
+};
+
+exports.mac_address_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  var cmd = 'cat /sys/class/net/' + nic_name + '/address';
+  trim_exec(cmd, cb);
+};
+
+exports.gateway_ip_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  trim_exec("ip r | grep " + nic_name + " | grep default | cut -d ' ' -f 3 | head -n1", cb);
+};
+
+exports.netmask_for = function(nic_name, cb) {
+  ensure_valid_nic(nic_name);
+  var cmd = "ifconfig " + nic_name + " 2> /dev/null | egrep 'netmask|Mask:' | awk '{print $4}' | sed 's/Mask://'";
+  trim_exec(cmd, cb);
+};
+
+exports.get_network_interfaces_list = function(cb) {
+
+  var count = 0,
+      list = [],
+      nics = os.networkInterfaces();
+
+  function append_data(obj) {
+    async.parallel([
+      function(cb) {
+        exports.mac_address_for(obj.name, cb)
+      },
+      function(cb) {
+        exports.gateway_ip_for(obj.name, cb)
+      },
+      function(cb) {
+        exports.netmask_for(obj.name, cb)
+      },
+      function(cb) {
+        exports.interface_type_for(obj.name, cb)
+      }
+    ], function(err, results) {
+      if (results[0]) obj.mac_address = results[0];
+      if (results[1]) obj.gateway_ip  = results[1];
+      if (results[2]) obj.netmask     = results[2];
+      if (results[3]) obj.type        = results[3];
+      
+      list.push(obj);
+      --count || cb(null, list);
+    })
+  }
+
+  for (var key in nics) {
+    if (key != 'lo0' && key != 'lo' && !key.match(/^tun/)) {
+
+      count++;
+      var obj = { name: key };
+
+      nics[key].forEach(function(type) {
+        if (type.family == 'IPv4') {
+          obj.ip_address = type.address;
+        }
+      });
+
+      append_data(obj);
+    }
+  }
+
+  if (count == 0)
+    cb(new Error('No interfaces found.'))
+}
+
diff --git a/lib/win32.js b/lib/win32.js
new file mode 100644
index 0000000..3519643
--- /dev/null
+++ b/lib/win32.js
@@ -0,0 +1,96 @@
+"use strict";
+
+var wmic = require('wmic'),
+    exec = require('child_process').exec,
+    os   = require('os');
+
+function get_wmic_ip_value(what, nic_name, cb){
+  exports.mac_address_for(nic_name, function(err, mac){
+    if (err || !mac)
+      return cb(err || new Error('No MAC Address found.'));
+
+    wmic.get_value('nicconfig', what, 'MACAddress = \'' + mac + '\'', function(err, out){
+      if (err) return cb(err);
+
+      cb(null, out.split(',')[0].replace(/[^0-9\.]/g, ''));
+    });
+  })
+}
+
+exports.get_active_network_interface_name = function(cb) {
+  wmic.get_value('nic', 'NetConnectionID', 'NetConnectionStatus = 2', cb);
+};
+
+exports.netmask_for = function(nic_name, cb) {
+  get_wmic_ip_value('IPSubnet', nic_name, cb);
+};
+
+exports.gateway_ip_for = function(nic_name, cb) {
+  get_wmic_ip_value('DefaultIPGateway', nic_name, cb);
+};
+
+exports.mac_address_for = function(nic_name, cb) {
+  var cond = 'NetConnectionID = \'' + nic_name + '\'';
+  wmic.get_value('nic', 'MACAddress', cond, cb);
+}
+
+exports.get_network_interfaces_list = function(callback) {
+
+  var count, 
+      list = [],
+      node_nics = os.networkInterfaces();
+
+  function done() {
+    --count || callback(null, list);
+  }
+
+  function set_gateway(obj) {
+    exports.gateway_ip_for(obj.name, function(err, res) {
+      obj.gateway_ip = res && res != '' ? res : null;
+      done();
+    })
+  }
+
+  function set_netmask(obj) {
+    exports.netmask_for(obj.name, function(err, res) {
+      obj.netmask = res && res != '' ? res : null;
+    })
+  }
+
+  wmic.get_list('nic', function(err, nics) {
+    if (err) return callback(err);    
+    count = nics.length;
+    if (count == 0){
+      return cb(new Error('No interfaces found.'))
+    }
+    nics.forEach(function(nic) {
+      if (nic.Name && nic.NetConnectionID != '' && nic.MACAddress != '') {
+
+        var obj = {
+          name: nic.NetConnectionID,
+          // description: nic.Name,
+          mac_address: nic.MACAddress,
+          ip_address: nic.IPAddress,
+          vendor: nic.Manufacturer,
+          model: nic.Description,
+          type: nic.Name.match(/wi-?fi|wireless/i) ? 'Wireless' : 'Wired'
+        }
+
+        var node_nic = node_nics[obj.name] || [];
+
+        node_nic.forEach(function(type) {
+          if (type.family == 'IPv4') {
+            obj.ip_address = type.address;
+          }
+        });
+
+        list.push(obj);
+        set_netmask(obj);        
+        set_gateway(obj);
+      } else {
+        done();
+      }
+    })
+  });
+
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3456939
--- /dev/null
+++ b/package.json
@@ -0,0 +1,27 @@
+{
+  "name": "network",
+  "version": "0.6.1",
+  "description": "Cross platform network utilities for Node.js (gateway_ip, MAC address, etc)",
+  "main": "./lib/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "bin": {
+    "network": "./bin/network"
+  },
+  "dependencies": {
+    "async": "^1.5.2",
+    "commander": "2.9.0",
+    "needle": "^3.0.0",
+    "wmic": "^1.0.1"
+  },
+  "author": {
+    "name": "Tomas Pollak",
+    "email": "tomas@forkhq.com"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/tomas/network.git"
+  },
+  "license": "MIT"
+}
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..6f39c00
--- /dev/null
+++ b/test.js
@@ -0,0 +1,26 @@
+var network = require('./lib');
+
+network.get_public_ip(function(err, res) {
+  console.log('Public IP --- ');
+  console.log(err || res);
+});
+
+network.get_private_ip(function(err, res) {
+  console.log('Private IP --- ');
+  console.log(err || res);
+});
+
+network.get_gateway_ip(function(err, res) {
+  console.log('Gateway IP --- ');
+  console.log(err || res);
+});
+
+network.get_active_interface(function(err, res) {
+  console.log('Active Network Interface --- ');
+  console.log(err || res);
+});
+
+network.get_interfaces_list(function(err, res) {
+  console.log('Network Interfaces List --- ');
+  console.log(err || res);
+});

Fixed code: *~
.DS_Store
node_modules
Network Utilies for Node.js
===========================

# Install

    $ npm install network

# Usage

## Get public IP

Returns your public IP address, as reported by DynDNS.org or other services.

``` js
var network = require('network');

network.get_public_ip(function(err, ip) {
  console.log(err || ip); // should return your public IP address
})
```

##### CLI

    $ network public_ip

## Get private IP

Returns the IP address assigned to your first active network inteface.

``` js
network.get_private_ip(function(err, ip) {
  console.log(err || ip); // err may be 'No active network interface found'.
})
```

##### CLI

    $ network private_ip

## Get gateway IP

Returns the IP of the gateway that your active network interface is linked to.

``` js
network.get_gateway_ip(function(err, ip) {
  console.log(err || ip); // err may be 'No active network interface found.'
})
```

##### CLI

    $ network gateway_ip

## Get active interface

Returns the IP, MAC address and interface type for the active network
interface. On OS X and Linux you also get the IP of its assigned gateway.

``` js
network.get_active_interface(function(err, obj) {

  /* obj should be:

  { name: 'eth0',
    ip_address: '10.0.1.3',
    mac_address: '56:e5:f9:e4:38:1d',
    type: 'Wired',
    netmask: '255.255.255.0',
    gateway_ip: '10.0.1.1' }

  */
})
```

##### CLI

    $ network active_interface

## Get interfaces list

Returns list of network interfaces, including MAC addresses and the such, just
as in the example above.

``` js
network.get_interfaces_list(function(err, list) {

  /* list should be:

  [{
    name: 'eth0',
    ip_address: '10.0.1.3',
    mac_address: '56:e5:f9:e4:38:1d',
    type: 'Wired',
    netmask: '255.255.255.0',
    gateway_ip: '10.0.1.1' 
   },
   { ... }, { ... }]

  */
})
```

##### CLI

    $ network interfaces_list

# Copyright

Written by Tomás Pollak. Copyright (c) Fork, Ltd.

# License

MIT.
#!/usr/bin/env node

var program = require('commander'),
    network = require('..'),
    pkg     = require('../package.json');

function print(results, json) {
  if (json) console.log(JSON.stringify(results, null, 2))
  else console.log(results)
}

program.version(pkg.version)

program
  .command('public_ip')
  .description('Print public ip address')
  .option('-j --json', 'Print results in JSON format')
  .on('--help', function () {
    console.log('  Examples:')
    console.log()
    console.log('    $ network public_ip')
    console.log()
  })
  .action(function () {
    network.get_public_ip(function (err, results) {
      if (err) {
        console.error(err)
        process.exit(1)
      }

      print(results, program.json)
      process.exit(0)
    })
  })

program
  .command('private_ip')
  .description('Print private ip address')
  .option('-j --json', 'Print results in JSON format')
  .on('--help', function () {
    console.log('  Examples:')
    console.log()
    console.log('    $ network private_ip')
    console.log()
  })
  .action(function () {
    network.get_private_ip(function (err, results) {
      if (err) {
        console.error(err)
        process.exit(1)
      }

      print(results, program.json)
      process.exit(0)
    })
  })

program
  .command('mac_address_for <interface>')
  .description('Print mac address for given network interface')
  .option('-j --json', 'Print results in JSON format')
  .on('--help', function () {
    console.log('  Examples:')
    console.log()
    console.log('    $ network mac_address_for Wi-Fi')
    console.log()
  })
  .action(function (nInterface) {
    network.mac_address_for(nInterface, function (err, results) {
      if (err) {
        console.error(err)
        process.exit(1)
      }

      print(results, program.json)
      process.exit(0)
    })
  })

program
  .command('gateway_ip')
  .description('Print gateway ip address')
  .option('-j --json', 'Print results in JSON format')
  .on('--help', function () {
    console.log('  Examples:')
    console.log()
    console.log('    $ network gateway_ip')
    console.log()
  })
  .action(function () {
    network.get_gateway_ip(function (err, results) {
      if (err) {
        console.error(err)
        process.exit(1)
      }

      print(results, program.json)
      process.exit(0)
    })
  })

program
  .command('active_interface')
  .description('Print active network interface')
  .option('-j --json', 'Print results in JSON format')
  .on('--help', function () {
    console.log('  Examples:')
    console.log()
    console.log('    $ network active_interface')
    console.log()
  })
  .action(function () {
    network.get_active_interface(function (err, results) {
      if (err) {
        console.error(err)
        process.exit(1)
      }

      print(results, program.json)
      process.exit(0)
    })
  })

program
  .command('interfaces_list')
  .description('Print all network interfaces')
  .option('-j --json', 'Print results in JSON format')
  .on('--help', function () {
    console.log('  Examples:')
    console.log()
    console.log('    $ network interfaces_list')
    console.log()
  })
  .action(function () {
    network.get_interfaces_list(function (err, results) {
      if (err) {
        console.error(err)
        process.exit(1)
      }

      print(results, program.json)
      process.exit(0)
    })
  })

program.parse(process.argv)

if (!program.args.length) program.help()
"use strict";

var os    = require('os'),
    exec  = require('child_process').exec,
    async = require('async');

//////////////////////////////////////////
// helpers

function trim_exec(cmd, cb) {
  exec(cmd, function(err, out) {
    if (out && out.toString() != '')
      cb(null, out.toString().trim())
    else
      cb(err)
  })
}

function ensure_valid_nic(str) {
  if (str.match(/[^\w]/))
    throw new Error("Invalid nic name given: " + str);
}

function determine_nic_type(str) {
  return str.match(/Ethernet/)
    ? "Wired"
    : str.match(/Wi-?Fi|AirPort/i)
    ? "Wireless"
    : str.match(/FireWire/)
    ? "FireWire"
    : str.match(/Thunderbolt/)
    ? "Thunderbolt"
    : str.match(/Bluetooth/)
    ? "Bluetooth"
    : str.match(/USB 10\/100\/1000 LAN/)
    ? "USB Ethernet Adapter"
    : "Other";
}

//////////////////////////////////////////
// exports

exports.get_active_network_interface_name = function(cb) {
  var cmd = "netstat -rn | grep UG | awk '{print $NF}'";
  exec(cmd, function(err, stdout) {
    if (err) return cb(err);

    var raw = stdout.toString().trim().split('\n');
    if (raw.length === 0 || raw === [''])
      return cb(new Error('No active network interface found.'));

    cb(null, raw[0]);
  });
};

/* unused

exports.interface_type_for = function(nic_name, cb) {
  exec('networksetup -listnetworkserviceorder | grep ' + nic_name, function(err, out) {
    if (err) return cb(err);

    var type = out.toString().match(/ethernet|lan/i) ? 'Wired' : 'Wireless';
    cb(null, type);
  })
};
*/

exports.mac_address_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  var cmd = "networksetup -getmacaddress " + nic_name + " | awk '{print $3}'";
  trim_exec(cmd, cb);
};

exports.gateway_ip_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  var cmd = "ipconfig getoption " + nic_name + " router";
  trim_exec(cmd, cb);
};

exports.netmask_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  var cmd = "ipconfig getoption " + nic_name + " subnet_mask";
  trim_exec(cmd, cb);
};
exports.status_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  var cmd = "netstat -rn | grep " + nic_name + ' | grep UG | wc -l | sed -e "s/1/active/" | sed -e "s/0/inactive/"'
  trim_exec(cmd, cb)
}

exports.get_network_interfaces_list = function(cb) {

  var count = 0,
      list  = [],
      nics  = os.networkInterfaces();

  function append_data(obj) {
    async.parallel([
      function(cb) {
        exports.gateway_ip_for(obj.name, cb)
      },
      function(cb) {
        exports.netmask_for(obj.name, cb)
      },
      function(cb){
        exports.status_for(obj.name, cb)
      }
    ], function(err, results) {
      if (results[0]) obj.gateway_ip = results[0];
      if (results[1]) obj.netmask    = results[1];
      if (results[2]) obj.status = results[2]
      
      list.push(obj);
      --count || cb(null, list);
    })
  }

  exec('networksetup -listallhardwareports', function(err, out) {
    if (err) return cb(err);

    var blocks = out.toString().split(/Hardware/).slice(1);
    count = blocks.length;

    blocks.forEach(function(block) {
      var parts = block.match(/Port: (.+)/),
          mac   = block.match(/Address: ([A-Fa-f0-9:-]+)/),
          name  = block.match(/Device: (\w+)/);
          

      if (!parts || !mac || !name) 
        return --count;

      var obj   = {},
          port  = parts[1];

      obj.name  = name[1];
      obj.desc  = port;
      obj.type  = determine_nic_type(port);
      obj.ip_address  = null;
      obj.mac_address = mac[1];

      (nics[obj.name] || []).forEach(function(type) {
        if (type.family == 'IPv4') {
          obj.ip_address = type.address;
        }
      });

      append_data(obj);
    })

    if (count == 0) cb(new Error('No interfaces found.'))
  })

};
"use strict";

var needle = require('needle');
var os_functions;
switch (process.platform) {
  case 'win32':
    os_functions = require('./win32');
    break;
  case 'linux':
    os_functions = require('./linux');
    break;
  case 'darwin':
    os_functions = require('./darwin');
    break;
}
// var ip_regex = /((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})/;
var ip_regex = /(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)/;

var public_ip_urls = [
  'http://checkip.dyndns.org',
  'http://wtfismyip.com/text',
  'http://ipecho.net/plain',
  'http://ifconfig.me/ip'
];

function is_ip_address(str) {
  return ip_regex.test(str);
};

function nic_by_name(name, cb) {
  os_functions.get_network_interfaces_list(function(err, list) {
    if (err) return cb(err);

    var nics = list.filter(function(nic) { return nic.name === name; });

    if (nics.length > 0)
      cb(null, nics[0]);
    else
      cb(new Error('No network interface named ' + name));
  });
};

exports.get_public_ip = function(options, cb) {

  if (typeof options == 'function') { // no options passed
    cb = options;
    options = {};
  }

  var urls = options.urls || public_ip_urls;

  function get(i) {
    var url = urls[i];
    if (!url) return cb(new Error('Unable to fetch IP address.'));

    needle.get(url, function(err, resp) {
      var body = resp && resp.body.toString();
      if (body && body.match(ip_regex)) {
        return cb(null, body.match(ip_regex)[1]);
      }

      get(i+1);
    })
  };

  get(0);
}

exports.get_private_ip = function(cb) {
  os_functions.get_network_interfaces_list(function(err, list) {
    if (err || !list)
      return cb(err || new Error('No network interfaces found.'));

    os_functions.get_active_network_interface_name(function(err, active_nic) {
      if (err) return cb(err);

      var ips = list.filter(function(nic) {
        if (is_ip_address(nic.ip_address))
          return active_nic ? active_nic == nic.name : true;
      });

      if (ips.length > 0)
        cb(null, ips[0].ip_address);
      else
        cb(new Error('No private IPs found (' + list.length + ' interfaces)'));
    });
  });
};

exports.get_gateway_ip = function(cb) {
  os_functions.get_active_network_interface_name(function(err, nic_name) {
    if (err || nic_name.trim() == '')
      return cb(err || new Error('No active network interface found.'));

    os_functions.gateway_ip_for(nic_name, function(err, out) {
      if (err || !out || out.toString() == '')
        return cb(err || new Error('No gateway IP assigned to ' + nic_name));

      cb(null, out.toString().trim())
    })
  });
};

exports.get_active_interface = function(cb) {
  os_functions.get_active_network_interface_name(function(err, nic_name) {
    if (err || !nic_name) return cb(err || new Error("No active interfaces detected."));

    nic_by_name(nic_name, function(err, nic) {
      if (err) return cb(err);

      os_functions.netmask_for(nic_name, function(err, netmask) {
        if (!err && netmask)
          nic.netmask = netmask.trim();

        os_functions.gateway_ip_for(nic_name, function(err, ip) {
          if (!err && ip)
            nic.gateway_ip = ip.toString().trim();

          cb(null, nic);
        })
      });
    });
  });
};

exports.get_interfaces_list = os_functions.get_network_interfaces_list;
exports.mac_address_for = os_functions.mac_address_for;
exports.gateway_ip_for = os_functions.gateway_ip_for;
"use strict";

var os    = require('os'),
    exec  = require('child_process').exec,
    async = require('async');

function trim_exec(cmd, cb) {
  exec(cmd, function(err, out) {
    if (out && out.toString() != '')
      cb(null, out.toString().trim())
    else
      cb(err)
  })
}

function ensure_valid_nic(str) {
  if (str.match(/[^\w]/))
    throw new Error("Invalid nic name given: " + str);
}

// If no wifi, then there is no error but cbed get's a null in second param.
exports.get_active_network_interface_name = function(cb) {
  var cmd = "netstat -rn | grep UG | awk '{print $NF}'";
  exec(cmd, function(err, stdout, stderr) {
    if (err) return cb(err);

    if (stderr.toString().match('not found')) {
      return cb(new Error('Command failed: ' + stderr.toString().trim()))
    }

    var raw = stdout.toString().trim().split('\n');
    if (raw.length === 0 || raw === [''])
      return cb(new Error('No active network interface found.'));

    cb(null, raw[0]);
  });
};

exports.interface_type_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  exec('cat /proc/net/wireless | grep ' + nic_name, function(err, out) {
    return cb(null, err ? 'Wired' : 'Wireless')
  })
};

exports.mac_address_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  var cmd = 'cat /sys/class/net/' + nic_name + '/address';
  trim_exec(cmd, cb);
};

exports.gateway_ip_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  trim_exec("ip r | grep " + nic_name + " | grep default | cut -d ' ' -f 3 | head -n1", cb);
};

exports.netmask_for = function(nic_name, cb) {
  ensure_valid_nic(nic_name);
  var cmd = "ifconfig " + nic_name + " 2> /dev/null | egrep 'netmask|Mask:' | awk '{print $4}' | sed 's/Mask://'";
  trim_exec(cmd, cb);
};

exports.get_network_interfaces_list = function(cb) {

  var count = 0,
      list = [],
      nics = os.networkInterfaces();

  function append_data(obj) {
    async.parallel([
      function(cb) {
        exports.mac_address_for(obj.name, cb)
      },
      function(cb) {
        exports.gateway_ip_for(obj.name, cb)
      },
      function(cb) {
        exports.netmask_for(obj.name, cb)
      },
      function(cb) {
        exports.interface_type_for(obj.name, cb)
      }
    ], function(err, results) {
      if (results[0]) obj.mac_address = results[0];
      if (results[1]) obj.gateway_ip  = results[1];
      if (results[2]) obj.netmask     = results[2];
      if (results[3]) obj.type        = results[3];
      
      list.push(obj);
      --count || cb(null, list);
    })
  }

  for (var key in nics) {
    if (key != 'lo0' && key != 'lo' && !key.match(/^tun/)) {

      count++;
      var obj = { name: key };

      nics[key].forEach(function(type) {
        if (type.family == 'IPv4') {
          obj.ip_address = type.address;
        }
      });

      append_data(obj);
    }
  }

  if (count == 0)
    cb(new Error('No interfaces found.'))
}

"use strict";

var wmic = require('wmic'),
    exec = require('child_process').exec,
    os   = require('os');

function get_wmic_ip_value(what, nic_name, cb){
  exports.mac_address_for(nic_name, function(err, mac){
    if (err || !mac)
      return cb(err || new Error('No MAC Address found.'));

    wmic.get_value('nicconfig', what, 'MACAddress = \'' + mac + '\'', function(err, out){
      if (err) return cb(err);

      cb(null, out.split(',')[0].replace(/[^0-9\.]/g, ''));
    });
  })
}

exports.get_active_network_interface_name = function(cb) {
  wmic.get_value('nic', 'NetConnectionID', 'NetConnectionStatus = 2', cb);
};

exports.netmask_for = function(nic_name, cb) {
  get_wmic_ip_value('IPSubnet', nic_name, cb);
};

exports.gateway_ip_for = function(nic_name, cb) {
  get_wmic_ip_value('DefaultIPGateway', nic_name, cb);
};

exports.mac_address_for = function(nic_name, cb) {
  var cond = 'NetConnectionID = \'' + nic_name + '\'';
  wmic.get_value('nic', 'MACAddress', cond, cb);
}

exports.get_network_interfaces_list = function(callback) {

  var count, 
      list = [],
      node_nics = os.networkInterfaces();

  function done() {
    --count || callback(null, list);
  }

  function set_gateway(obj) {
    exports.gateway_ip_for(obj.name, function(err, res) {
      obj.gateway_ip = res && res != '' ? res : null;
      done();
    })
  }

  function set_netmask(obj) {
    exports.netmask_for(obj.name, function(err, res) {
      obj.netmask = res && res != '' ? res : null;
    })
  }

  wmic.get_list('nic', function(err, nics) {
    if (err) return callback(err);    
    count = nics.length;
    if (count == 0){
      return cb(new Error('No interfaces found.'))
    }
    nics.forEach(function(nic) {
      if (nic.Name && nic.NetConnectionID != '' && nic.MACAddress != '') {

        var obj = {
          name: nic.NetConnectionID,
          // description: nic.Name,
          mac_address: nic.MACAddress,
          ip_address: nic.IPAddress,
          vendor: nic.Manufacturer,
          model: nic.Description,
          type: nic.Name.match(/wi-?fi|wireless/i) ? 'Wireless' : 'Wired'
        }

        var node_nic = node_nics[obj.name] || [];

        node_nic.forEach(function(type) {
          if (type.family == 'IPv4') {
            obj.ip_address = type.address;
          }
        });

        list.push(obj);
        set_netmask(obj);        
        set_gateway(obj);
      } else {
        done();
      }
    })
  });

};
{
  "name": "network",
  "version": "0.6.1",
  "description": "Cross platform network utilities for Node.js (gateway_ip, MAC address, etc)",
  "main": "./lib/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "bin": {
    "network": "./bin/network"
  },
  "dependencies": {
    "async": "^1.5.2",
    "commander": "2.9.0",
    "needle": "^3.0.0",
    "wmic": "^1.0.1"
  },
  "author": {
    "name": "Tomas Pollak",
    "email": "tomas@forkhq.com"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/tomas/network.git"
  },
  "license": "MIT"
}
var network = require('./lib');

network.get_public_ip(function(err, res) {
  console.log('Public IP --- ');
  console.log(err || res);
});

network.get_private_ip(function(err, res) {
  console.log('Private IP --- ');
  console.log(err || res);
});

network.get_gateway_ip(function(err, res) {
  console.log('Gateway IP --- ');
  console.log(err || res);
});

network.get_active_interface(function(err, res) {
  console.log('Active Network Interface --- ');
  console.log(err || res);
});

network.get_interfaces_list(function(err, res) {
  console.log('Network Interfaces List --- ');
  console.log(err || res);
});