UNPKG

3.18 kBJavaScriptView Raw
1"use strict";
2
3const {isIP} = require("net");
4const {networkInterfaces} = require("os");
5const execa = require("execa");
6
7const gwArgs = "path Win32_NetworkAdapterConfiguration where IPEnabled=true get DefaultIPGateway,GatewayCostMetric,IPConnectionMetric,Index /format:table".split(" ");
8const ifArgs = index => `path Win32_NetworkAdapter where Index=${index} get NetConnectionID,MACAddress /format:table`.split(" ");
9
10const spawnOpts = {
11 windowsHide: true,
12};
13
14// Parsing tables like this. The final metric is GatewayCostMetric + IPConnectionMetric
15//
16// DefaultIPGateway GatewayCostMetric Index IPConnectionMetric
17// {"1.2.3.4", "2001:db8::1"} {0, 256} 12 25
18// {"2.3.4.5"} {25} 12 55
19function 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${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
43function 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 // try to get the interface name by matching the mac to os.networkInterfaces to avoid wmic's encoding issues
50 // https://github.com/silverwind/default-gateway/issues/14
51 for (const [osname, addrs] of Object.entries(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
61const 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
78const 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
95module.exports.v4 = () => promise("v4");
96module.exports.v6 = () => promise("v6");
97
98module.exports.v4.sync = () => sync("v4");
99module.exports.v6.sync = () => sync("v6");