116 lines
4.8 KiB
JavaScript
116 lines
4.8 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
const components = require("./components");
|
|
|
|
// General regexps
|
|
// const FIND_ADDRESS_INSTR_REGEX = /([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}\s{0,}\{[\s\n\r0-9a-z;:\/\."',]{0,}\}/gmi;
|
|
const DOMAINER_REGEX = /domain\s{1,}[a-z0-9\.]{1,}\s{1,}\{[\s\n\r0-9a-z;:\/\."',]{0,}\}/gmi;
|
|
const DOMAINER_VALUE_FIND_REGEX = /domain\s{1,}[a-z0-9\.]{1,}\s{1,}\{/gmi;
|
|
const ADDRESS_WITH_PORT_REGEX = /([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}/mi;
|
|
const FIND_ADDRESS_INSTR_REGEX = /([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}\s{0,}\{([\s\n\r0-9a-z;:\/\."',]|domain\s{1,}[a-z0-9\.]{1,}\s{1,}\{[\s\n\r0-9a-z;:\/\."',]{0,}\}){0,}\}/gmi;
|
|
const COMMENT_REGEX = /#.{0,}(\n|\r|$)/gmi;
|
|
const CONTENT_INTO_CURLY_BRACES = /\{[\s\n\r0-9a-z;:\/\."',]{0,}\}{0,}\}/gmi;
|
|
|
|
// Instruction regexps
|
|
function readInstructions (instructions, fullCode, forDomains=false) {
|
|
const syntaxTree = { firewall: false, encryption: {} };
|
|
|
|
for (let instruction of instructions) {
|
|
if (!instruction) {
|
|
if (instructions.indexOf(instruction) === instructions.length - 1) {
|
|
break;
|
|
}
|
|
throw new SyntaxError("Invalid syntax: `;;`");
|
|
}
|
|
const instructionParts = instruction.split(/\s{1,}/);
|
|
const mainOperator = instructionParts[0];
|
|
switch (mainOperator) {
|
|
case "type":
|
|
if (instructionParts.length !== 2)
|
|
throw new SyntaxError("Invalid syntax of operator `type`: type <type_of_connection>");
|
|
const SelectedComponent = components[instructionParts[1]];
|
|
if (SelectedComponent === undefined)
|
|
throw new SyntaxError(`Unknown component: ${instructionParts[1]}. Valid components: ${Object.keys(components).join(", ")}`);
|
|
syntaxTree.Type = SelectedComponent;
|
|
break;
|
|
case "target":
|
|
if (instructionParts.length !== 2)
|
|
throw new SyntaxError("Invalid syntax of operator `target`: target <connection_target>");
|
|
syntaxTree.target = instructionParts[1];
|
|
break;
|
|
case "cert":
|
|
if (instructionParts.length < 3)
|
|
throw new SyntaxError("Invalid syntax of operator `cert`: cert <type:(public|private)> <path_to_cert>");
|
|
if (!["public", "private"].includes(instructionParts[1]))
|
|
throw new SyntaxError("Cert type can be only private or public, not " + instructionParts[1]);
|
|
syntaxTree.encryption[instructionParts[1]] = instructionParts.slice(2,).join(" ");
|
|
break;
|
|
case "auth":
|
|
if (instructionParts.length < 4 || instructionParts.length < 2)
|
|
throw new SyntaxError("Invalid syntax of operator `auth`: auth <mode:(loopback|strict)> <login> <password> OR auth null");
|
|
if (!["loopback", "strict", "null"].includes(instructionParts[1]))
|
|
throw new SyntaxError("Invalid mode: " + instructionParts[1] + ", valid modes: loopback, strict, null");
|
|
syntaxTree.auth = instructionParts[1] !== "null" ? {
|
|
mode: instructionParts[1],
|
|
login: instructionParts[2],
|
|
password: instructionParts.slice(3,).join(" ")
|
|
} : null;
|
|
break;
|
|
case "firewall":
|
|
break;
|
|
case "#instuction:domain":
|
|
if (forDomains)
|
|
throw new SyntaxError("Nested use of domains doesn't not supported");
|
|
break;
|
|
default:
|
|
throw new SyntaxError(`Unknown routemap's operator: ${mainOperator}`);
|
|
}
|
|
}
|
|
|
|
if (Object.keys(syntaxTree.encryption).length === 0)
|
|
syntaxTree.encryption = null;
|
|
else if (Object.keys(syntaxTree.encryption).length === 1)
|
|
throw new SyntaxError("No private or public key specified");
|
|
|
|
if (!forDomains) {
|
|
syntaxTree.domains = new Object();
|
|
const allDomains = Object.fromEntries((fullCode.match(DOMAINER_REGEX) ?? [])
|
|
.map(x => [x.match(DOMAINER_VALUE_FIND_REGEX)[0].split(/\s{1,}/gmi)[1], x.match(CONTENT_INTO_CURLY_BRACES)[0].slice(1, -1).split(/\s{0,};\s{0,}/g).map(x => x.trim())]));
|
|
|
|
for (let domain in allDomains) {
|
|
syntaxTree.domains[domain] = readInstructions(allDomains[domain], "", true);
|
|
}
|
|
}
|
|
|
|
return syntaxTree;
|
|
}
|
|
|
|
function logger (...p) {
|
|
//return;
|
|
console.debug("[DEBUG.routemap]:", ...p);
|
|
}
|
|
|
|
module.exports.parse = function () {
|
|
const syntaxTree = new Object();
|
|
const routemapContent = fs.readFileSync(path.join(__dirname, "./routemap.txt"), { encoding: "utf-8" })
|
|
.replace(COMMENT_REGEX, "\n");
|
|
const findedAdrInstructions = routemapContent.match(FIND_ADDRESS_INSTR_REGEX);
|
|
logger("findedAdrInstructions:", findedAdrInstructions);
|
|
if (findedAdrInstructions === null)
|
|
throw new SyntaxError("routemap.txt is empty");
|
|
for (let itemAdrInst of findedAdrInstructions) {
|
|
itemAdrInst = itemAdrInst.replace(/[\n\r]{1,}/g, "");
|
|
const content = itemAdrInst
|
|
.replace(ADDRESS_WITH_PORT_REGEX, "")
|
|
.replace(DOMAINER_REGEX, "#instuction:domain")
|
|
.trim()
|
|
.slice(1, -1)
|
|
.split(/\s{0,};\s{0,}/g).map(x => x.trim());
|
|
logger("itemAdrInst:", itemAdrInst, "with content:", content);
|
|
const localSyntaxTree = readInstructions(content, itemAdrInst);
|
|
syntaxTree[itemAdrInst.match(ADDRESS_WITH_PORT_REGEX)[0]] = localSyntaxTree;
|
|
}
|
|
logger("Full syntax tree:", syntaxTree);
|
|
return syntaxTree;
|
|
}
|