UNPKG

wireguard-tools.js

Version:

Control your wireguard interface from node.js, not a wireguard-tools wrapper!

172 lines (171 loc) 8.57 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getConfig = exports.setConfig = exports.deleteInterface = exports.listDevices = exports.constants = void 0; const path_1 = __importDefault(require("path")); const readline_1 = __importDefault(require("readline")); const fs_1 = require("fs"); const net_1 = require("net"); const promises_1 = require("stream/promises"); if (process.platform === "win32") global.WIREGUARD_DLL_PATH = path_1.default.join(__dirname, "../addons/tools/win/wireguard-nt/bin", process.arch === "x64" ? "amd64" : process.arch, "wireguard.dll"); const addon = require("../libs/prebuildifyLoad.cjs")("wginterface", path_1.default.resolve(__dirname, "../")); exports.constants = addon.constants; /** default location to run socket's */ const defaultPath = (process.env.WIRWGUARD_GO_RUN || "").length > 0 ? path_1.default.resolve(process.cwd(), process.env.WIRWGUARD_GO_RUN) : process.platform === "win32" ? "\\\\.\\pipe\\WireGuard" : "/var/run/wireguard"; async function exists(path) { return fs_1.promises.open(path).then(o => o && (o.close().then(() => true, () => true)) || true, () => false); } /** * Get Wireguard devices and locations */ async function listDevices() { let devices = []; if (typeof addon.listDevices === "function") devices = devices.concat(await addon.listDevices()); if (await exists(defaultPath)) (await fs_1.promises.readdir(defaultPath)).forEach(file => devices.push({ from: "userspace", name: file.endsWith(".sock") ? file.slice(0, -5) : file, path: path_1.default.join("/var/run/wireguard", file) })); return devices; } exports.listDevices = listDevices; async function deleteInterface(wgName) { if (typeof addon.deleteInterface === "function") return addon.deleteInterface(wgName); return fs_1.promises.rm(path_1.default.join(defaultPath, (wgName).concat(".sock")), { force: true }); } exports.deleteInterface = deleteInterface; /** * Set Wireguard config in interface * @param wgName - Interface name * @param config - Interface config */ async function setConfig(wgName, config) { if (typeof addon.setConfig === "function") return addon.setConfig(wgName, config); const client = (0, net_1.createConnection)(path_1.default.join(defaultPath, (wgName).concat(".sock"))); const writel = (...data) => client.write(data.map(String).join("").concat("\n")); // Init set config in interface writel("set=1"); // Port listening if (config.portListen !== undefined && Math.floor(config.portListen) >= 0) writel(("listen_port="), ((Math.floor(config.portListen)))); // fwmark if (Math.floor(config.fwmark) >= 0) writel(("fwmark="), ((Math.floor(config.fwmark)))); // Replace peer's if (config.replacePeers) writel("replace_peers=true"); // Keys if (typeof config.privateKey === "string" && config.privateKey.length > 0) writel(("private_key="), (Buffer.from(config.privateKey, "base64").toString("hex"))); // Mount peer for (const publicKey of Object.keys(config.peers || {})) { const { presharedKey, endpoint, keepInterval, removeMe, allowedIPs = [] } = config.peers[publicKey]; // Public key writel(("public_key="), (Buffer.from(publicKey, "base64").toString("hex"))); if (removeMe) { writel("remove=true"); // Remove peer continue; } if (typeof endpoint === "string" && endpoint.length > 0) writel(("endpoint="), (endpoint)); if (typeof presharedKey === "string" && presharedKey.length > 3) writel(("preshared_key="), (Buffer.from(presharedKey, "base64").toString("hex"))); if (typeof keepInterval === "number" && Math.floor(keepInterval) > 0) writel(("persistent_keepalive_interval="), (String(Math.floor(keepInterval)))); if (allowedIPs.length > 0) { writel("replace_allowed_ips=true"); const fixed = allowedIPs.map(i => i.indexOf("/") === -1 ? i.concat("/", ((0, net_1.isIPv4)(i) ? "32" : "128")) : i); for (const IIP of fixed) writel(("allowed_ip="), (IIP)); } } let payload = ""; client.once("data", function processBuff(buff) { payload = payload.concat(buff.toString("utf8")); if (payload[payload.length - 1] === "\n" && payload[payload.length - 2] === "\n") { client.end(); // Close conenction return; } client.once("data", processBuff); }); client.write("\n"); await (0, promises_1.finished)(client, { error: true }); const payloadKeys = payload.split("\n").filter(i => i.length > 3).map(line => { const iit = line.indexOf("="); return [line.slice(0, iit), line.slice(iit + 1)]; }); if (payloadKeys.at(-1)[1] !== "0") { const err = new Error("Invalid send config, check log"); throw err; } } exports.setConfig = setConfig; /** * Get wireguard interface config * @param wgName - Interface name * @returns */ async function getConfig(wgName) { if (typeof addon.getConfig === "function") return addon.getConfig(wgName); const info = (await listDevices()).find(int => int.name === wgName); if (!info) throw new Error("Create interface, not exists"); const client = (0, net_1.createConnection)(path_1.default.join(defaultPath, wgName.concat(".sock"))); const config = Object(); let latestPeer, previewKey; const tetrisBreak = readline_1.default.createInterface(client); tetrisBreak.on("line", function lineProcess(line) { if (line === "") tetrisBreak.removeListener("line", lineProcess).close(); const findout = line.indexOf("="), keyName = line.slice(0, findout), value = line.slice(findout + 1); if (findout <= 0) return; if (keyName === "errno" && value !== "0") throw new Error(("wireguard-go error, code: ").concat(value)); // Drop if ((["last_handshake_time_nsec", "protocol_version", "errno"]).includes(keyName)) return; else if (keyName === "private_key") config.privateKey = Buffer.from(value, "hex").toString("base64"); else if (keyName === "listen_port") config.portListen = Number(value); else if (keyName === "endpoint") ((config.peers || (config.peers = {}))[latestPeer]).endpoint = value; else if (keyName === "persistent_keepalive_interval") ((config.peers || (config.peers = {}))[latestPeer]).keepInterval = Number(value); else if (keyName === "rx_bytes") ((config.peers || (config.peers = {}))[latestPeer]).rxBytes = Number(value); else if (keyName === "tx_bytes") ((config.peers || (config.peers = {}))[latestPeer]).txBytes = Number(value); else if (keyName === "last_handshake_time_sec") ((config.peers || (config.peers = {}))[latestPeer]).lastHandshake = new Date(Number(value) * 1000); else if (keyName === "allowed_ip") { if (!value) return; ((config.peers || (config.peers = {}))[latestPeer]).allowedIPs = (((config.peers || (config.peers = {}))[latestPeer]).allowedIPs || []).concat(value); } else if (keyName === "preshared_key") { if (value === "0000000000000000000000000000000000000000000000000000000000000000") return; ((config.peers || (config.peers = {}))[latestPeer]).presharedKey = Buffer.from(value, "hex").toString("base64"); } else if (keyName === "public_key") { const keyDecode = Buffer.from(value, "hex").toString("base64"); if (previewKey !== "public_key") (config.peers || (config.peers = {}))[latestPeer] = {}; else { config.publicKey = latestPeer; (config.peers || (config.peers = {}))[keyDecode] = (config.peers || (config.peers = {}))[latestPeer]; delete (config.peers || (config.peers = {}))[latestPeer]; latestPeer = keyDecode; } } previewKey = keyName; }); client.write("get=1\n\n"); await new Promise((done, reject) => tetrisBreak.on("error", reject).once("close", done)); await (0, promises_1.finished)(client.end()); return config; } exports.getConfig = getConfig;