diff --git a/backend/services/network.js b/backend/services/network.js index 228214a..34d7274 100644 --- a/backend/services/network.js +++ b/backend/services/network.js @@ -4,7 +4,7 @@ const axios = require("axios"); const api = require("../utils/controller-api"); const db = require("../utils/db"); const constants = require("../utils/constants"); -const startStopDNS = require("../utils/zns"); +const zns = require("../utils/zns"); async function getNetworkAdditionalData(data) { let additionalData = db @@ -22,11 +22,22 @@ async function getNetworkAdditionalData(data) { delete data.remoteTraceLevel; delete data.remoteTraceTarget; + let ad = { ...additionalData.value() }; + data.dns = { + domain: ad.dnsDomain, + servers: ["pippo"], + }; + if (ad.dnsIP) data.dns["servers"].push(ad.dnsIP); + console.log(`*** dns="${JSON.stringify(ad)}" -> ${JSON.stringify(data.dns)}`); + delete ad.dnsIP; + delete ad.dnsDomain; + delete ad.dnsEnable; + delete ad.dnsWildcard; return { id: data.id, type: "Network", clock: Math.floor(new Date().getTime() / 1000), - ...additionalData.value(), + ...ad, config: data, }; } @@ -47,13 +58,11 @@ async function getNetworksData(nwids) { return []; }); - let data = Promise.all( + return Promise.all( multipleRes.map((el) => { return getNetworkAdditionalData(el.data); }) ); - - return data; } exports.createNetworkAdditionalData = createNetworkAdditionalData; @@ -84,6 +93,12 @@ async function updateNetworkAdditionalData(nwid, data) { additionalData.rulesSource = data.rulesSource; } if (data.hasOwnProperty("dnsEnable")) { + if (data.dnsEnable) { + //TODO: start ZeroNSd and get its IP address + additionalData.dnsIP = "127.0.0.1"; + } else { + additionalData.dnsIP = null; + } additionalData.dnsEnable = data.dnsEnable; } if (data.hasOwnProperty("dnsDomain")) { @@ -101,17 +116,7 @@ async function updateNetworkAdditionalData(nwid, data) { .write(); if (data.hasOwnProperty("dnsEnable")) { - let token = db.get("users").value()[0]; - token = token.token; - let config = db.get("networks").find({ id: nwid }).value(); - let ret = startStopDNS(token, config.id, config.additionalConfig); - if (ret) { - db.get("networks") - .filter({ id: nwid }) - .map("additionalConfig") - .map((additionalConfig) => _.assign(additionalConfig, ret)) - .write(); - } + zns.handleNet(db.get("networks").filter({ id: nwid }).value()[0]); } } } diff --git a/backend/utils/zns.js b/backend/utils/zns.js index 9982f43..731f167 100644 --- a/backend/utils/zns.js +++ b/backend/utils/zns.js @@ -1,64 +1,147 @@ -const { spawn } = require("child_process"); +const path = require("path"); +const fs = require("fs"); -function pidIsRunning(pid) { - try { - process.kill(pid, 0); - return true; - } catch (e) { - return false; - } +const db = require("../utils/db"); +const cp = require("child_process"); + +//TODO: does this kind of "optimization" make sense in Node.js? +let token = null; +function getToken() { + if (!token) + try { + token = db.get("users").value()[0].token; + } catch { + console.warn("*** token retrieval failed"); + } + return token; } +function setPid(nwid, pid) { + db.get("networks") + .filter({ id: nwid }) + .map("additionalConfig") + .map((additionalConfig) => (additionalConfig.pidDNS = pid)) + .write(); +} + +const isRunning = (query, pid) => { + return new Promise(function (resolve) { + //FIXME: Check if pgrep is available + cp.exec(`pgrep ${query}`, (err, stdout) => { + resolve(stdout.indexOf(`${pid}`) > -1); + }); + }); +}; + function startDNS(token, nwid, conf) { - if (conf.hasOwnProperty("pidDNS")) { - if (pidIsRunning(conf.pidDNS)) { - return null; + //FIXME: check it does the right thing when conf.pidDNS is null/undefined + isRunning("zeronsd", conf.pidDNS).then((ok) => { + if (ok) { + console.log( + `startDNS(${token}, ${nwid}): already active on PID ${conf.pidDNS}` + ); + } else { + let cmd = "zeronsd"; + let opts = Array(); + if (process.geteuid() === 0) { + // production in Docker container + } else { + // we are debugging + let myLocal = "/home/mcon/.cargo/bin"; + let pth = process.env.PATH.split(path.delimiter); + if (!pth.includes(myLocal)) pth.push(myLocal); + if ( + !process.env.PATH.split(path.delimiter).some(function (d) { + let e = path.resolve(d, cmd); + console.log(`*** PATH testing: "${d}" -> "${e}"`); + try { + fs.accessSync(e, fs.constants.X_OK); + console.log(" is executable"); + cmd = "sudo"; + opts.push("-E", e); + return true; + } catch (e) { + console.warn(` cannot execute (${e})`); + return false; + } + }) + ) { + console.error(`*** zeronsd not found in PATH (${process.env.PATH})`); + return; + } + } + opts.push("start", "-v", "-v"); + if (conf.hasOwnProperty("dnsWildcard") && conf.dnsWildcard) { + opts.push("-w"); + } + if (conf.hasOwnProperty("dnsDomain") && !!conf.dnsDomain) { + opts.push("-d", conf.dnsDomain); + } + opts.push(nwid); + process.env.ZEROTIER_CENTRAL_TOKEN = token; + console.log(`*** PATH: "${process.env.PATH}"`); + console.log( + `*** ZEROTIER_CENTRAL_TOKEN: "${process.env.ZEROTIER_CENTRAL_TOKEN}"` + ); + let dns = cp.spawn(cmd, opts, { detached: true }); + dns.on("spawn", () => { + console.log( + `zeronsd successfully spawned [${dns.pid}](${dns.spawnargs})` + ); + setPid(nwid, dns.pid); + }); + dns.stdout.on("data", (data) => { + console.log(`zeronsd spawn stdout: ${data}`); + }); + dns.stderr.on("data", (data) => { + console.error(`zeronsd spawn stderr: ${data}`); + }); + dns.on("error", (error) => { + console.log(`zeronsd spawn ERROR: [${error}](${dns.spawnargs})`); + }); + dns.on("close", (code) => { + console.log(`zeronsd exited: [${code}](${dns.spawnargs})`); + setPid(nwid, null); + }); } - delete conf.pidDNS; - } - const opts = Array("start"); - if (conf.hasOwnProperty("dnsWildcard") && conf.dnsWildcard) { - opts.push("-w"); - } - if (conf.hasOwnProperty("dnsDomain") && !!conf.dnsDomain) { - opts.push("-d", conf.dnsDomain); - } - opts.push(nwid); - let env = process.env; - env.ZEROTIER_CENTRAL_TOKEN = token; - console.log(`*** PATH: "${env.PATH}"`); - const dns = spawn("zeronsd", opts, { - detached: true, - env: env, }); - dns.on("spawn", () => { - console.log(`zeronsd successfully spawned [${dns.pid}](${dns.spawnargs})`); - console.log("*** should update PID ***"); - }); - dns.on("error", (error) => { - console.log(`zeronsd spawn ERROR: [${error}](${dns.spawnargs})`); - }); - conf.pidDNS = dns.pid; - return conf; } -function stopDNS(conf) { - if (conf.hasOwnProperty("pidDNS")) { - try { - process.kill(conf.pidDNS); - } catch (e) {} - delete conf.pidDNS; - return conf; - } - return null; -} - -module.exports = function (token, nwid, conf) { - let ret; - if (conf.dnsEnable) { - ret = startDNS(token, nwid, conf); +function stopDNS(nwid, conf) { + let pid = conf.pidDNS; + if (pid) { + isRunning("zeronsd", pid).then((ok) => { + if (ok) { + console.log(`stopDNS(${nwid}): stopping PID ${pid}`); + try { + process.kill(pid); + } catch (e) { + console.error(`stopDNS(${nwid}): stopping PID ${pid} FAILED (${e})`); + } + } else { + console.log(`stopDNS(${nwid}): PID ${pid} is stale`); + } + }); + setPid(nwid, null); } else { - ret = stopDNS(conf); + console.log(`stopDNS(${nwid}): net has no PID`); } - return ret; -}; +} + +exports.handleNet = handleNet; +function handleNet(net) { + let cfg = net.additionalConfig; + if (cfg.dnsEnable) { + startDNS(getToken(), net.id, cfg); + } else { + stopDNS(net.id, cfg); + } +} + +exports.scan = scan; +function scan() { + let nets = db.get("networks").value(); + nets.forEach((net) => { + handleNet(net); + }); +} diff --git a/frontend/src/components/NetworkSettings/NetworkSettings.jsx b/frontend/src/components/NetworkSettings/NetworkSettings.jsx index 54ff624..855a1be 100644 --- a/frontend/src/components/NetworkSettings/NetworkSettings.jsx +++ b/frontend/src/components/NetworkSettings/NetworkSettings.jsx @@ -41,6 +41,11 @@ function NetworkSettings({ network, setNetwork }) { sendReq(data); }; + console.log( + `*** dns="${JSON.stringify(network)}" -> ${JSON.stringify( + network["config"] + )}` + ); return ( }>