'use strict'; exports = module.exports = { getBlocklist, setBlocklist, getDynamicDns, setDynamicDns, }; const assert = require('assert'), BoxError = require('./boxerror.js'), cron = require('./cron.js'), ipaddr = require('ipaddr.js'), path = require('path'), paths = require('./paths.js'), safe = require('safetydance'), settings = require('./settings.js'), shell = require('./shell.js'), validator = require('validator'); const SET_BLOCKLIST_CMD = path.join(__dirname, 'scripts/setblocklist.sh'); async function getBlocklist() { const value = await settings.getBlob(settings.FIREWALL_BLOCKLIST_KEY); return value ? value.toString('utf8') : ''; } async function setBlocklist(blocklist, auditSource) { assert.strictEqual(typeof blocklist, 'string'); assert.strictEqual(typeof auditSource, 'object'); const parsedIp = ipaddr.process(auditSource.ip); for (const line of blocklist.split('\n')) { if (!line || line.startsWith('#')) continue; const rangeOrIP = line.trim(); // this checks for IPv4 and IPv6 if (!validator.isIP(rangeOrIP) && !validator.isIPRange(rangeOrIP)) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} is not a valid IP or range`); if (rangeOrIP.indexOf('/') === -1) { if (auditSource.ip === rangeOrIP) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} includes client IP. Cannot block yourself`); } else { const parsedRange = ipaddr.parseCIDR(rangeOrIP); // returns [addr, range] if (parsedRange[0].kind() === parsedIp.kind() && parsedIp.match(parsedRange)) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} includes client IP. Cannot block yourself`); } } if (settings.isDemo()) throw new BoxError(BoxError.CONFLICT, 'Not allowed in demo mode'); // store in blob since the value field is TEXT and has 16kb size limit await settings.setBlob(settings.FIREWALL_BLOCKLIST_KEY, Buffer.from(blocklist)); // this is done only because it's easier for the shell script and the firewall service to get the value if (!safe.fs.writeFileSync(paths.FIREWALL_BLOCKLIST_FILE, blocklist + '\n', 'utf8')) throw new BoxError(BoxError.FS_ERROR, safe.error.message); const [error] = await safe(shell.promises.sudo('setBlocklist', [ SET_BLOCKLIST_CMD ], {})); if (error) throw new BoxError(BoxError.IPTABLES_ERROR, `Error setting blocklist: ${error.message}`); } async function getDynamicDns() { const enabled = await settings.get(settings.DYNAMIC_DNS_KEY); return enabled ? !!enabled : false; // db holds string values only } async function setDynamicDns(enabled) { assert.strictEqual(typeof enabled, 'boolean'); await settings.set(settings.DYNAMIC_DNS_KEY, enabled ? 'enabled' : ''); // db holds string values only cron.dynamicDnsChanged(enabled); }