1 | "use strict";
|
2 |
|
3 | const execa = require("execa");
|
4 | const os = require("os");
|
5 | const net = require("net");
|
6 |
|
7 | const gwArgs = "path Win32_NetworkAdapterConfiguration where IPEnabled=true get DefaultIPGateway,GatewayCostMetric,IPConnectionMetric,Index /format:table".split(" ");
|
8 | const ifArgs = index => `path Win32_NetworkAdapter where Index=${index} get NetConnectionID,MACAddress /format:table`.split(" ");
|
9 |
|
10 | const spawnOpts = {
|
11 | windowsHide: true,
|
12 | };
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | function parseGwTable(gwTable, family) {
|
20 | let [bestGw, bestMetric, bestId] = [null, null, null];
|
21 |
|
22 | for (let line of (gwTable || "").trim().split(/\r?\n/).splice(1)) {
|
23 | line = line.trim();
|
24 | const [_, gwArr, gwCostsArr, id, ipMetric] = /({.+?}) +?({.+?}) +?([0-9]+) +?([0-9]+)/g.exec(line) || [];
|
25 | if (!gwArr) continue;
|
26 |
|
27 | const gateways = (gwArr.match(/"(.+?)"/g) || []).map(match => match.substring(1, match.length - 1));
|
28 | const gatewayCosts = (gwCostsArr.match(/[0-9]+/g) || []);
|
29 |
|
30 | for (const [index, gateway] of Object.entries(gateways)) {
|
31 | if (!gateway || `v${net.isIP(gateway)}` !== family) continue;
|
32 |
|
33 | const metric = parseInt(gatewayCosts[index]) + parseInt(ipMetric);
|
34 | if (!bestGw || metric < bestMetric) {
|
35 | [bestGw, bestMetric, bestId] = [gateway, metric, id];
|
36 | }
|
37 | }
|
38 | }
|
39 |
|
40 | if (bestGw) return [bestGw, bestId];
|
41 | }
|
42 |
|
43 | function parseIfTable(ifTable) {
|
44 | const line = (ifTable || "").trim().split("\n")[1];
|
45 |
|
46 | let [mac, name] = line.trim().split(/\s+/);
|
47 | mac = mac.toLowerCase();
|
48 |
|
49 |
|
50 |
|
51 | for (const [osname, addrs] of Object.entries(os.networkInterfaces())) {
|
52 | for (const addr of addrs) {
|
53 | if (addr && addr.mac && addr.mac.toLowerCase() === mac) {
|
54 | return osname;
|
55 | }
|
56 | }
|
57 | }
|
58 | return name;
|
59 | }
|
60 |
|
61 | const promise = async family => {
|
62 | const {stdout} = await execa("wmic", gwArgs, spawnOpts);
|
63 | const [gateway, id] = parseGwTable(stdout, family) || [];
|
64 |
|
65 | if (!gateway) {
|
66 | throw new Error("Unable to determine default gateway");
|
67 | }
|
68 |
|
69 | let name;
|
70 | if (id) {
|
71 | const {stdout} = await execa("wmic", ifArgs(id), spawnOpts);
|
72 | name = parseIfTable(stdout);
|
73 | }
|
74 |
|
75 | return {gateway, interface: name ? name : null};
|
76 | };
|
77 |
|
78 | const sync = family => {
|
79 | const {stdout} = execa.sync("wmic", gwArgs, spawnOpts);
|
80 | const [gateway, id] = parseGwTable(stdout, family) || [];
|
81 |
|
82 | if (!gateway) {
|
83 | throw new Error("Unable to determine default gateway");
|
84 | }
|
85 |
|
86 | let name;
|
87 | if (id) {
|
88 | const {stdout} = execa.sync("wmic", ifArgs(id), spawnOpts);
|
89 | name = parseIfTable(stdout);
|
90 | }
|
91 |
|
92 | return {gateway, interface: name ? name : null};
|
93 | };
|
94 |
|
95 | module.exports.v4 = () => promise("v4");
|
96 | module.exports.v6 = () => promise("v6");
|
97 |
|
98 | module.exports.v4.sync = () => sync("v4");
|
99 | module.exports.v6.sync = () => sync("v6");
|