From ff4d7fdacee4cc7b9170760d74fbf0548304a5bd Mon Sep 17 00:00:00 2001 From: FullGreaM Date: Sun, 19 Jan 2025 04:50:56 +0300 Subject: [PATCH] Works on http-reverse auth, add cookie-parser module, add simple support of custom modules --- components/http-reverse.js | 9 ++++++- modules/example.js | 10 +++++++ modules/example_custom_auth.js | 43 +++++++++++++++++++++++++++++ modules/index.js | 49 ++++++++++++++++++++++++++++++++++ package-lock.json | 21 +++++++++++++++ package.json | 1 + routemap-parser.js | 45 ++++++++++++++++++++++++++++--- routemap.txt | 8 +++++- 8 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 modules/example.js create mode 100644 modules/example_custom_auth.js create mode 100644 modules/index.js diff --git a/components/http-reverse.js b/components/http-reverse.js index bfbee4d..d2dc378 100644 --- a/components/http-reverse.js +++ b/components/http-reverse.js @@ -8,9 +8,10 @@ const url = require('url'); const path = require('path'); const fs = require('fs'); const fetch = require('node-fetch'); +const cookieParser = require('cookie-parser') function logger (...p) { - //return; + return; console.log("[DEBUG.main]:", ...p); } @@ -115,6 +116,12 @@ module.exports = class HTTPReverse extends Component { authPage(this, req, res, next) }); + this.app.use(cookieParser()); + + this.syntax.routelist.forEach(route => { + this.app.use(route); + }); + this.app.use("*", async (req, res) => { logger("REQ HEADERS:>", req.headers); logger("originalUrl:>", req.originalUrl); diff --git a/modules/example.js b/modules/example.js new file mode 100644 index 0000000..32b2847 --- /dev/null +++ b/modules/example.js @@ -0,0 +1,10 @@ +const NetProxierModule = require("./"); + +module.exports = class ExampleMdoule extends NetProxierModule { + constructor () { + super(); + this.operators.push(new this.statics.Operator("testi", () => true, () => { + console.log("BEEP"); + })); + } +}; diff --git a/modules/example_custom_auth.js b/modules/example_custom_auth.js new file mode 100644 index 0000000..154cc82 --- /dev/null +++ b/modules/example_custom_auth.js @@ -0,0 +1,43 @@ +const express = require("express"); +const NetProxierModule = require("./"); + +const HTTPReverse = require("../components/http-reverse"); + +module.exports = class CustomAuthPage extends NetProxierModule { + constructor () { + super(); + + const cauth = new this.statics.Operator("cauth", (argv) => { + return argv.length === 2; + }, (argv, self, syntaxTree) => { + if (![HTTPReverse].includes(syntaxTree.Type)) { + throw new SyntaxError("Unsupported type of server"); + } + const [ login, password ] = argv; + const router = express.Router(); + + router.get("/auth/:redirectTo", (req, res, next) => { + if (req.cookies.auth === login + ":" + password) + return next(); + const endless = req.query.login + ":" + req.query.password; + const redirectTo = Buffer.from(req.params.redirectTo, "hex").toString("utf8"); + //console.log("redirect to:", redirectTo); + if (endless === login + ":" + password) { + res.cookie("auth", endless); + } + res.redirect(redirectTo); + }); + + router.use((req, res, next) => { + //console.log('Cookies: ', req.cookies); + if (req.cookies.auth === login + ":" + password) + return next(); + res.send('

Needs auth


Логин:

Пароль:

'); + }); + + syntaxTree.routelist.push(router); + }); + + this.operators.push(cauth); + } +}; diff --git a/modules/index.js b/modules/index.js new file mode 100644 index 0000000..7e9c4b5 --- /dev/null +++ b/modules/index.js @@ -0,0 +1,49 @@ +// Func + +class OperatorsStorage extends Array { + constructor (...p) { + super(...p); + this._indexObject = {}; + this._indexObjectIndexOf = {}; + } + + push (item) { + if (item instanceof Operator) { + if (!this._indexObject[item.operator]) { + super.push(item); + this._indexObject[item.operator] = item; + } + } + else + throw new TypeError("Into OperatorsStorage you can add only Operator's instances"); + } + + includes (item) { + if (item instanceof Operator) { + return this._indexObject[item.operator] !== undefined; + } + else + throw new TypeError("Into OperatorsStorage storage only Operator type"); + } +} + +// API + +class Operator { + constructor (operatorName, argvHandler = () => true, exec = (argv, module) => {}) { + this.operator = operatorName; + this.handlers = { + argv: argvHandler, + exec + }; + } +} + +module.exports = class NetProxierModule { + constructor () { + this.operators = new OperatorsStorage(); + + this.statics = new Object(); + this.statics.Operator = Operator; + } +} diff --git a/package-lock.json b/package-lock.json index 05caf17..ba008a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "body": "^5.1.0", "body-parser": "^1.20.3", + "cookie-parser": "^1.4.7", "express": "^4.21.2", "node-fetch": "^2.6.7", "sse": "^0.0.8" @@ -237,6 +238,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", diff --git a/package.json b/package.json index e1c5237..cd2feb6 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dependencies": { "body": "^5.1.0", "body-parser": "^1.20.3", + "cookie-parser": "^1.4.7", "express": "^4.21.2", "node-fetch": "^2.6.7", "sse": "^0.0.8" diff --git a/routemap-parser.js b/routemap-parser.js index 9eb1d40..b594788 100644 --- a/routemap-parser.js +++ b/routemap-parser.js @@ -4,16 +4,17 @@ 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_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 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; +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: {} }; + const syntaxTree = { firewall: false, encryption: {}, modules: new Set(), routelist: [] }; + const moduleOpsQueue = new Array(); for (let instruction of instructions) { if (!instruction) { @@ -24,6 +25,7 @@ function readInstructions (instructions, fullCode, forDomains=false) { } const instructionParts = instruction.split(/\s{1,}/); const mainOperator = instructionParts[0]; + switch (mainOperator) { case "type": if (instructionParts.length !== 2) @@ -58,11 +60,36 @@ function readInstructions (instructions, fullCode, forDomains=false) { break; case "firewall": break; + case "import": + if (instructionParts.length < 2) + throw new SyntaxError("Invalid syntax of operator `import`: import OR import , , ..., "); + const modules = instructionParts.slice(1,).join(" ").split(/\s{0,},\s{0,}/gmi); + modules.forEach(m => syntaxTree.modules.add(new (require(`./modules/${m}`))())); + break; case "#instuction:domain": if (forDomains) throw new SyntaxError("Nested use of domains doesn't not supported"); break; default: + let mopFlag = false + for (let module of syntaxTree.modules) { + for (let op of module.operators) { + if (op.operator === mainOperator) { + // Нуждается в доработке! + if (!op.handlers.argv(instructionParts.slice(1,))) + throw new SyntaxError("Custom module's operator error. Invalid argv syntax on operator: " + mainOperator); + + moduleOpsQueue.push( + (st) => op.handlers.exec(instructionParts.slice(1,), module, st) + ); + mopFlag = true; + break; + } + } + if (mopFlag) + break; + } + if (mopFlag) break; throw new SyntaxError(`Unknown routemap's operator: ${mainOperator}`); } } @@ -82,6 +109,16 @@ function readInstructions (instructions, fullCode, forDomains=false) { } } + syntaxTree.modules = Array.from(syntaxTree.modules); + + // Object.assign needs replace to object which give only-read access + // Except: routelist + moduleOpsQueue.forEach(h => { + const st = Object.assign({}, syntaxTree); + st.routelist = syntaxTree.routelist; + return h(st); + }); + return syntaxTree; } diff --git a/routemap.txt b/routemap.txt index a34bbe5..cc19486 100644 --- a/routemap.txt +++ b/routemap.txt @@ -6,12 +6,18 @@ type revHTTP; target https://github.com; }; + domain domain.loc { + import example; + testi; + }; } 127.0.0.1:7889 { + import example, example_custom_auth; type revHTTP; target https://google.com; - auth loopback admin qwerty; + #auth loopback admin qwerty; + cauth admin 123; cert public ./tests/pub.crt; cert private ./tests/priv.key; }