UNPKG

wireguard-tools.js

Version:

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

231 lines (230 loc) 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WgQuick = exports.stringify = exports.parse = void 0; const net_1 = require("net"); const wginterface_1 = require("./wginterface"); const key_1 = require("./key"); const crypto_1 = require("crypto"); function parse(config) { if (Buffer.isBuffer(config)) config = config.toString(); const lines = config.trim().split(/\r?\n/).map(s => s.trim()).filter(s => (!!s) && !(s.startsWith("#"))); return Object.assign({}, { peers: {} }, lines.reduce((acc, line, index) => { if (line.toLowerCase() === "[peer]") { if (typeof acc.at(-1).start === "undefined") acc.at(-1).start = index; else { acc.at(-1).end = index - 1; acc.push({ start: index }); } } return acc; }, [{ start: 0 }]).map(({ start, end }) => { const [target, ...linesConfig] = lines.slice(start, end); return { target: target.toLowerCase() === "[peer]" ? "peer" : "interface", linesConfig: linesConfig.map(s => { const ii = s.indexOf("="), keyName = s.substring(0, ii).trim(), value = s.substring(ii + 1).trim(); return { keyName, value }; }) }; }).reduce((acc, info) => { if (info.target === "interface") { for (const { keyName, value } of info.linesConfig) { if (keyName.toLowerCase() === "privatekey") acc.privateKey = value; else if (keyName.toLowerCase() === "publickey") acc.publicKey = value; else if (keyName.toLowerCase() === "address") acc.Address = ([acc.Address, value]).flat(3).map(s => s && s.split(",")).flat(2).map(s => s && s.trim()).filter(Boolean); else if (keyName.toLowerCase() === "dns") acc.DNS = ([acc.DNS, value]).flat(3).map(s => s && s.split(",")).flat(2).map(s => s && s.trim()).filter(Boolean); else if (keyName.toLowerCase() === "listenport") { const port = parseInt(value); if (port >= 1 && port <= ((2 ** 16) - 1)) acc.portListen = port; } } } else if (info.target === "peer") { acc.peers = (acc.peers || {}); const publicKey = info.linesConfig.find(s => s.keyName.toLowerCase() === "publickey")?.value; if (!!publicKey) { for (const { keyName, value } of info.linesConfig) { if (keyName.toLowerCase() === "publickey") continue; else if (keyName.toLowerCase() === "allowedips") (acc.peers[publicKey] || (acc.peers[publicKey] = {})).allowedIPs = ([acc.peers[publicKey].allowedIPs, value]).flat(3).map(s => s && s.split(",")).flat(2).map(s => s && s.trim()).filter(Boolean); else if (keyName.toLowerCase() === "presharedkey") (acc.peers[publicKey] || (acc.peers[publicKey] = {})).presharedKey = value; else if (keyName.toLowerCase() === "endpoint") (acc.peers[publicKey] || (acc.peers[publicKey] = {})).endpoint = value; } } } return acc; }, {})); } exports.parse = parse; function stringify({ portListen = 0, privateKey, publicKey, Address, DNS = ["1.1.1.1", "8.8.8.8"], peers = {} }) { const configString = ["[Interface]"]; if (typeof publicKey === "string" && publicKey.length === wginterface_1.constants.WG_B64_LENGTH) configString.push(("PublicKey = ").concat(publicKey)); if (typeof privateKey === "string" && privateKey.length === wginterface_1.constants.WG_B64_LENGTH) configString.push(("PrivateKey = ").concat(privateKey)); if (portListen >= 0 && portListen <= ((2 ** 16) - 1)) configString.push(("ListenPort = ").concat(String(portListen))); if (Array.isArray(Address) && Address.length > 0) configString.push(("Address = ").concat(...(Address.join(", ")))); if (Array.isArray(DNS) && DNS.length > 0) configString.push(("DNS = ").concat(DNS.join(", "))); // Peers mount for (const pubKey of Object.keys(peers)) { configString.push("", "[Peer]", ("PublicKey = ").concat(pubKey)); const { presharedKey, allowedIPs, keepInterval, endpoint } = peers[pubKey]; if (typeof presharedKey === "string" && presharedKey.length > 0) configString.push(("PresharedKey = ").concat(presharedKey)); if (typeof endpoint === "string" && endpoint.length > 0) configString.push(("Endpoint = ").concat(endpoint)); if (typeof keepInterval === "number" && keepInterval > 0) configString.push(("PersistentKeepalive = ").concat(String(keepInterval))); if (Array.isArray(allowedIPs) && allowedIPs.length > 0) configString.push(("AllowedIPs = ").concat(allowedIPs.join(", "))); } return configString.join("\n").trim(); } exports.stringify = stringify; /** * Create and Manipulate interface Configs */ class WgQuick extends Map { _privateKey; /** * Set private key to interface */ set privateKey(key) { if (key.length === wginterface_1.constants.WG_B64_LENGTH) { this._privateKey = key; (0, key_1.publicKey)(key).then(key => this._publicKey = key, () => { }); } else throw new Error("Invalid private key"); } /** * Get private key from interface config */ get privateKey() { if (String(this._privateKey).length === wginterface_1.constants.WG_B64_LENGTH) return this._privateKey; return undefined; } _publicKey; /** * Set public key to interface */ set publicKey(key) { if (key.length === wginterface_1.constants.WG_B64_LENGTH) this._publicKey = key; else throw new Error("Invalid public key"); } /** * Get public key from interface config */ get publicKey() { if (String(this._publicKey).length === wginterface_1.constants.WG_B64_LENGTH) return this._publicKey; return undefined; } _portListen = (0, crypto_1.randomInt)(2000, 65535); /** Get port to listen, 0 less a 65535 */ set portListen(port) { if (port >= 1 && port <= 65535) this._portListen = port; else if (port === 0) this._portListen = (port = (0, crypto_1.randomInt)(2000, 65535)); else throw new Error("Set valid range port 0 to or equal at a 65535"); } get portListen() { if (this._portListen <= 0) return (this._portListen = (0, crypto_1.randomInt)(2000, 65535)); return this._portListen || 0; } _Address = ["10.0.0.1/24"]; get Address() { return this._Address || []; } set Address(addr) { this._Address = Array.isArray(addr) ? addr.filter(s => !!((0, net_1.isIP)(s.split("/")[0]))) : []; } _DNS = ["1.1.1.1", "8.8.8.8"]; get DNS() { return this._DNS || []; } set DNS(dns) { this._DNS = Array.isArray(dns) ? dns.filter(s => !!((0, net_1.isIP)(s.split("/")[0]))) : []; } constructor(config) { super(); if (!config) return; const { privateKey, publicKey, portListen, Address, DNS, peers } = config; this._portListen = portListen; this.privateKey = privateKey; this._publicKey = publicKey; this.Address = Array.isArray(Address) ? Address.filter(s => !!((0, net_1.isIP)(s.split("/")[0]))) : []; this.DNS = Array.isArray(DNS) ? DNS.filter(s => !!((0, net_1.isIP)(s))) : []; for (const pubKey in (peers || {})) if (pubKey.length === wginterface_1.constants.WG_B64_LENGTH && peers[pubKey]) this.set(pubKey, peers[pubKey]); } set() { if (arguments.length === 0 || typeof arguments[0] === "boolean" || typeof arguments[0] === "number") { let preshareKey = !!arguments[1]; if (typeof arguments[0] === "number") { if (!(arguments[0] > 0)) throw new Error("Set less 1 or equal"); return Promise.all(Array(arguments[0]).fill(undefined).map(async () => this.set(preshareKey))); } preshareKey = !!arguments[0]; return Promise.resolve().then(async () => { let peerKey; while (!peerKey) { peerKey = await (0, key_1.genKey)(true); if (this.has(peerKey.publicKey)) peerKey = undefined; } super.set(peerKey.publicKey, { ...(preshareKey ? { presharedKey: peerKey.presharedKey } : {}), keepInterval: 5, allowedIPs: [], }); return peerKey.publicKey; }); } if (String(arguments[0] || "").length !== wginterface_1.constants.WG_B64_LENGTH) throw new Error("Set valid peer public key!"); const { allowedIPs = [], endpoint, keepInterval = 0, presharedKey } = (arguments[1] || {}); super.set(arguments[0], { ...(String(presharedKey || "").length === wginterface_1.constants.WG_B64_LENGTH ? { presharedKey } : {}), ...(String(endpoint || "").length >= 1 ? { endpoint } : {}), keepInterval, allowedIPs, }); return this; } toJSON() { const { _privateKey, _publicKey, _Address, _DNS, _portListen } = this; return { ...(_privateKey ? { privateKey: _privateKey } : {}), ...(_publicKey ? { publicKey: _publicKey } : {}), ...(_Address ? { Address: _Address } : { Address: [] }), portListen: _portListen, ...(_DNS ? { DNS: _DNS } : { DNS: [] }), peers: Array.from(this.entries()).reduce((acc, [pubKey, config]) => Object.assign(acc, { [pubKey]: config }), {}), }; } toString() { return stringify(this.toJSON()); } } exports.WgQuick = WgQuick;