| 1 | 'use strict';
|
|---|
| 2 |
|
|---|
| 3 | var os = require('os');
|
|---|
| 4 | var fs = require('fs');
|
|---|
| 5 | var child = require('child_process');
|
|---|
| 6 |
|
|---|
| 7 | var DEFAULT_RESOLV_FILE = '/etc/resolv.conf';
|
|---|
| 8 |
|
|---|
| 9 | function getInterfaceName() {
|
|---|
| 10 | var val = 'eth';
|
|---|
| 11 | var platform = os.platform();
|
|---|
| 12 | if (platform === 'darwin') {
|
|---|
| 13 | val = 'en';
|
|---|
| 14 | } else if (platform === 'win32') {
|
|---|
| 15 | val = null;
|
|---|
| 16 | }
|
|---|
| 17 | return val;
|
|---|
| 18 | }
|
|---|
| 19 |
|
|---|
| 20 | function getIfconfigCMD() {
|
|---|
| 21 | if (os.platform() === 'win32') {
|
|---|
| 22 | return 'ipconfig/all';
|
|---|
| 23 | }
|
|---|
| 24 | return '/sbin/ifconfig';
|
|---|
| 25 | }
|
|---|
| 26 |
|
|---|
| 27 | // typeof os.networkInterfaces family is a number (v18.0.0)
|
|---|
| 28 | // types: 'IPv4' | 'IPv6' => 4 | 6
|
|---|
| 29 | // @see https://github.com/nodejs/node/issues/42861
|
|---|
| 30 | function matchName(actualFamily, expectedFamily) {
|
|---|
| 31 | if (expectedFamily === 'IPv4') {
|
|---|
| 32 | return actualFamily === 'IPv4' || actualFamily === 4;
|
|---|
| 33 | }
|
|---|
| 34 | if (expectedFamily === 'IPv6') {
|
|---|
| 35 | return actualFamily === 'IPv6' || actualFamily === 6;
|
|---|
| 36 | }
|
|---|
| 37 | return actualFamily === expectedFamily;
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | /**
|
|---|
| 41 | * Get all addresses.
|
|---|
| 42 | *
|
|---|
| 43 | * @param {String} [interfaceName] interface name, default is 'eth' on linux, 'en' on mac os.
|
|---|
| 44 | * @param {Function(err, addr)} callback
|
|---|
| 45 | * - {Object} addr {
|
|---|
| 46 | * - {String} ip
|
|---|
| 47 | * - {String} ipv6
|
|---|
| 48 | * - {String} mac
|
|---|
| 49 | * }
|
|---|
| 50 | */
|
|---|
| 51 | function address(interfaceName, callback) {
|
|---|
| 52 | if (typeof interfaceName === 'function') {
|
|---|
| 53 | callback = interfaceName;
|
|---|
| 54 | interfaceName = null;
|
|---|
| 55 | }
|
|---|
| 56 |
|
|---|
| 57 | var addr = {
|
|---|
| 58 | ip: address.ip(interfaceName),
|
|---|
| 59 | ipv6: address.ipv6(interfaceName),
|
|---|
| 60 | mac: null
|
|---|
| 61 | };
|
|---|
| 62 | address.mac(interfaceName, function (err, mac) {
|
|---|
| 63 | if (mac) {
|
|---|
| 64 | addr.mac = mac;
|
|---|
| 65 | }
|
|---|
| 66 | callback(err, addr);
|
|---|
| 67 | });
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| 70 | address.interface = function (family, name) {
|
|---|
| 71 | var interfaces = os.networkInterfaces();
|
|---|
| 72 | var noName = !name;
|
|---|
| 73 | name = name || getInterfaceName();
|
|---|
| 74 | family = family || 'IPv4';
|
|---|
| 75 | for (var i = -1; i < 8; i++) {
|
|---|
| 76 | var interfaceName = name + (i >= 0 ? i : ''); // support 'lo' and 'lo0'
|
|---|
| 77 | var items = interfaces[interfaceName];
|
|---|
| 78 | if (items) {
|
|---|
| 79 | for (var j = 0; j < items.length; j++) {
|
|---|
| 80 | var item = items[j];
|
|---|
| 81 | if (matchName(item.family, family)) {
|
|---|
| 82 | return item;
|
|---|
| 83 | }
|
|---|
| 84 | }
|
|---|
| 85 | }
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 | if (noName) {
|
|---|
| 89 | // filter all loopback or local addresses
|
|---|
| 90 | for (var k in interfaces) {
|
|---|
| 91 | var items = interfaces[k];
|
|---|
| 92 | for (var i = 0; i < items.length; i++) {
|
|---|
| 93 | var item = items[i];
|
|---|
| 94 | // all 127 addresses are local and should be ignored
|
|---|
| 95 | if (matchName(item.family, family) && !item.address.startsWith('127.')) {
|
|---|
| 96 | return item;
|
|---|
| 97 | }
|
|---|
| 98 | }
|
|---|
| 99 | }
|
|---|
| 100 | }
|
|---|
| 101 | return;
|
|---|
| 102 | };
|
|---|
| 103 |
|
|---|
| 104 | /**
|
|---|
| 105 | * Get current machine IPv4
|
|---|
| 106 | *
|
|---|
| 107 | * @param {String} [interfaceName] interface name, default is 'eth' on linux, 'en' on mac os.
|
|---|
| 108 | * @return {String} IP address
|
|---|
| 109 | */
|
|---|
| 110 | address.ip = function (interfaceName) {
|
|---|
| 111 | var item = address.interface('IPv4', interfaceName);
|
|---|
| 112 | return item && item.address;
|
|---|
| 113 | };
|
|---|
| 114 |
|
|---|
| 115 | /**
|
|---|
| 116 | * Get current machine IPv6
|
|---|
| 117 | *
|
|---|
| 118 | * @param {String} [interfaceName] interface name, default is 'eth' on linux, 'en' on mac os.
|
|---|
| 119 | * @return {String} IP address
|
|---|
| 120 | */
|
|---|
| 121 | address.ipv6 = function (interfaceName) {
|
|---|
| 122 | var item = address.interface('IPv6', interfaceName);
|
|---|
| 123 | return item && item.address;
|
|---|
| 124 | };
|
|---|
| 125 |
|
|---|
| 126 | // osx start line 'en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500'
|
|---|
| 127 | // linux start line 'eth0 Link encap:Ethernet HWaddr 00:16:3E:00:0A:29 '
|
|---|
| 128 | var MAC_OSX_START_LINE = /^(\w+)\:\s+flags=/;
|
|---|
| 129 | var MAC_LINUX_START_LINE = /^(\w+)\s{2,}link encap:\w+/i;
|
|---|
| 130 |
|
|---|
| 131 | // ether 78:ca:39:b0:e6:7d
|
|---|
| 132 | // HWaddr 00:16:3E:00:0A:29
|
|---|
| 133 | var MAC_RE = address.MAC_RE = /(?:ether|HWaddr)\s+((?:[a-z0-9]{2}\:){5}[a-z0-9]{2})/i;
|
|---|
| 134 |
|
|---|
| 135 | // osx: inet 192.168.2.104 netmask 0xffffff00 broadcast 192.168.2.255
|
|---|
| 136 | // linux: inet addr:10.125.5.202 Bcast:10.125.15.255 Mask:255.255.240.0
|
|---|
| 137 | var MAC_IP_RE = address.MAC_IP_RE = /inet\s(?:addr\:)?(\d+\.\d+\.\d+\.\d+)/;
|
|---|
| 138 |
|
|---|
| 139 | function getMAC(content, interfaceName, matchIP) {
|
|---|
| 140 | var lines = content.split('\n');
|
|---|
| 141 | for (var i = 0; i < lines.length; i++) {
|
|---|
| 142 | var line = lines[i].trimRight();
|
|---|
| 143 | var m = MAC_OSX_START_LINE.exec(line) || MAC_LINUX_START_LINE.exec(line);
|
|---|
| 144 | if (!m) {
|
|---|
| 145 | continue;
|
|---|
| 146 | }
|
|---|
| 147 |
|
|---|
| 148 | // check interface name
|
|---|
| 149 | var name = m[1];
|
|---|
| 150 | if (name.indexOf(interfaceName) !== 0) {
|
|---|
| 151 | continue;
|
|---|
| 152 | }
|
|---|
| 153 |
|
|---|
| 154 | var ip = null;
|
|---|
| 155 | var mac = null;
|
|---|
| 156 | var match = MAC_RE.exec(line);
|
|---|
| 157 | if (match) {
|
|---|
| 158 | mac = match[1];
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | i++;
|
|---|
| 162 | while (true) {
|
|---|
| 163 | line = lines[i];
|
|---|
| 164 | if (!line || MAC_OSX_START_LINE.exec(line) || MAC_LINUX_START_LINE.exec(line)) {
|
|---|
| 165 | i--;
|
|---|
| 166 | break; // hit next interface, handle next interface
|
|---|
| 167 | }
|
|---|
| 168 | if (!mac) {
|
|---|
| 169 | match = MAC_RE.exec(line);
|
|---|
| 170 | if (match) {
|
|---|
| 171 | mac = match[1];
|
|---|
| 172 | }
|
|---|
| 173 | }
|
|---|
| 174 |
|
|---|
| 175 | if (!ip) {
|
|---|
| 176 | match = MAC_IP_RE.exec(line);
|
|---|
| 177 | if (match) {
|
|---|
| 178 | ip = match[1];
|
|---|
| 179 | }
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | i++;
|
|---|
| 183 | }
|
|---|
| 184 |
|
|---|
| 185 | if (ip === matchIP) {
|
|---|
| 186 | return mac;
|
|---|
| 187 | }
|
|---|
| 188 | }
|
|---|
| 189 | }
|
|---|
| 190 |
|
|---|
| 191 | /**
|
|---|
| 192 | * Get current machine MAC address
|
|---|
| 193 | *
|
|---|
| 194 | * @param {String} [interfaceName] interface name, default is 'eth' on linux, 'en' on mac os.
|
|---|
| 195 | * @param {Function(err, address)} callback
|
|---|
| 196 | */
|
|---|
| 197 | address.mac = function (interfaceName, callback) {
|
|---|
| 198 | if (typeof interfaceName === 'function') {
|
|---|
| 199 | callback = interfaceName;
|
|---|
| 200 | interfaceName = null;
|
|---|
| 201 | }
|
|---|
| 202 | interfaceName = interfaceName || getInterfaceName();
|
|---|
| 203 | var item = address.interface('IPv4', interfaceName);
|
|---|
| 204 | if (!item) {
|
|---|
| 205 | return callback();
|
|---|
| 206 | }
|
|---|
| 207 |
|
|---|
| 208 | // https://github.com/nodejs/node/issues/13581
|
|---|
| 209 | // bug in node 7.x and <= 8.4.0
|
|---|
| 210 | if (!process.env.CI && (item.mac === 'ff:00:00:00:00:00' || item.mac === '00:00:00:00:00:00')) {
|
|---|
| 211 | // wrong address, ignore it
|
|---|
| 212 | item.mac = '';
|
|---|
| 213 | }
|
|---|
| 214 |
|
|---|
| 215 | if (item.mac) {
|
|---|
| 216 | return callback(null, item.mac);
|
|---|
| 217 | }
|
|---|
| 218 |
|
|---|
| 219 | child.exec(getIfconfigCMD(), {timeout: 5000}, function (err, stdout, stderr) {
|
|---|
| 220 | if (err || !stdout) {
|
|---|
| 221 | return callback(err);
|
|---|
| 222 | }
|
|---|
| 223 |
|
|---|
| 224 | var mac = getMAC(stdout || '', interfaceName, item.address);
|
|---|
| 225 | callback(null, mac);
|
|---|
| 226 | });
|
|---|
| 227 | };
|
|---|
| 228 |
|
|---|
| 229 | // nameserver 172.24.102.254
|
|---|
| 230 | var DNS_SERVER_RE = /^nameserver\s+(\d+\.\d+\.\d+\.\d+)$/i;
|
|---|
| 231 |
|
|---|
| 232 | /**
|
|---|
| 233 | * Get DNS servers.
|
|---|
| 234 | *
|
|---|
| 235 | * @param {String} [filepath] resolv config file path. default is '/etc/resolv.conf'.
|
|---|
| 236 | * @param {Function(err, servers)} callback
|
|---|
| 237 | */
|
|---|
| 238 | address.dns = function (filepath, callback) {
|
|---|
| 239 | if (typeof filepath === 'function') {
|
|---|
| 240 | callback = filepath;
|
|---|
| 241 | filepath = null;
|
|---|
| 242 | }
|
|---|
| 243 | filepath = filepath || DEFAULT_RESOLV_FILE;
|
|---|
| 244 | fs.readFile(filepath, 'utf8', function (err, content) {
|
|---|
| 245 | if (err) {
|
|---|
| 246 | return callback(err);
|
|---|
| 247 | }
|
|---|
| 248 | var servers = [];
|
|---|
| 249 | content = content || '';
|
|---|
| 250 | var lines = content.split('\n');
|
|---|
| 251 | for (var i = 0; i < lines.length; i++) {
|
|---|
| 252 | var line = lines[i].trim();
|
|---|
| 253 | var m = DNS_SERVER_RE.exec(line);
|
|---|
| 254 | if (m) {
|
|---|
| 255 | servers.push(m[1]);
|
|---|
| 256 | }
|
|---|
| 257 | }
|
|---|
| 258 |
|
|---|
| 259 | callback(null, servers);
|
|---|
| 260 | });
|
|---|
| 261 | };
|
|---|
| 262 |
|
|---|
| 263 | module.exports = address;
|
|---|