UNPKG

6.67 kBJavaScriptView Raw
1'use strict';
2
3var os = require('os');
4var fs = require('fs');
5var child = require('child_process');
6
7var DEFAULT_RESOLV_FILE = '/etc/resolv.conf';
8
9function 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
20function 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
30function 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 */
51function 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
70address.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 */
110address.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 */
121address.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 '
128var MAC_OSX_START_LINE = /^(\w+)\:\s+flags=/;
129var 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
133var 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
137var MAC_IP_RE = address.MAC_IP_RE = /inet\s(?:addr\:)?(\d+\.\d+\.\d+\.\d+)/;
138
139function 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 */
197address.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
230var 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 */
238address.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
263module.exports = address;