Works on http-reverse auth, add cookie-parser module, add simple support of custom modules

This commit is contained in:
FullGreaM 2025-01-19 04:50:56 +03:00
parent 0a9355bc9d
commit ff4d7fdace
8 changed files with 180 additions and 6 deletions

View File

@ -8,9 +8,10 @@ const url = require('url');
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const cookieParser = require('cookie-parser')
function logger (...p) { function logger (...p) {
//return; return;
console.log("[DEBUG.main]:", ...p); console.log("[DEBUG.main]:", ...p);
} }
@ -115,6 +116,12 @@ module.exports = class HTTPReverse extends Component {
authPage(this, req, res, next) authPage(this, req, res, next)
}); });
this.app.use(cookieParser());
this.syntax.routelist.forEach(route => {
this.app.use(route);
});
this.app.use("*", async (req, res) => { this.app.use("*", async (req, res) => {
logger("REQ HEADERS:>", req.headers); logger("REQ HEADERS:>", req.headers);
logger("originalUrl:>", req.originalUrl); logger("originalUrl:>", req.originalUrl);

10
modules/example.js Normal file
View File

@ -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");
}));
}
};

View File

@ -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('<form action=\"/auth/'+ Buffer.from(req.originalUrl).toString("hex") +'\"><h4>Needs auth</h4><hr/><p>Логин: <input type="text" size="40" name="login"></p><p>Пароль: <input type="password" size="40" name="password"></p><p><input type="submit"></p></form>');
});
syntaxTree.routelist.push(router);
});
this.operators.push(cauth);
}
};

49
modules/index.js Normal file
View File

@ -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;
}
}

21
package-lock.json generated
View File

@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"body": "^5.1.0", "body": "^5.1.0",
"body-parser": "^1.20.3", "body-parser": "^1.20.3",
"cookie-parser": "^1.4.7",
"express": "^4.21.2", "express": "^4.21.2",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"sse": "^0.0.8" "sse": "^0.0.8"
@ -237,6 +238,26 @@
"node": ">= 0.6" "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": { "node_modules/cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",

View File

@ -22,6 +22,7 @@
"dependencies": { "dependencies": {
"body": "^5.1.0", "body": "^5.1.0",
"body-parser": "^1.20.3", "body-parser": "^1.20.3",
"cookie-parser": "^1.4.7",
"express": "^4.21.2", "express": "^4.21.2",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"sse": "^0.0.8" "sse": "^0.0.8"

View File

@ -4,16 +4,17 @@ const components = require("./components");
// General regexps // 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 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 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 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 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 // Instruction regexps
function readInstructions (instructions, fullCode, forDomains=false) { 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) { for (let instruction of instructions) {
if (!instruction) { if (!instruction) {
@ -24,6 +25,7 @@ function readInstructions (instructions, fullCode, forDomains=false) {
} }
const instructionParts = instruction.split(/\s{1,}/); const instructionParts = instruction.split(/\s{1,}/);
const mainOperator = instructionParts[0]; const mainOperator = instructionParts[0];
switch (mainOperator) { switch (mainOperator) {
case "type": case "type":
if (instructionParts.length !== 2) if (instructionParts.length !== 2)
@ -58,11 +60,36 @@ function readInstructions (instructions, fullCode, forDomains=false) {
break; break;
case "firewall": case "firewall":
break; break;
case "import":
if (instructionParts.length < 2)
throw new SyntaxError("Invalid syntax of operator `import`: import <module_name> OR import <module_name1>, <module_name2>, ..., <module_name3>");
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": case "#instuction:domain":
if (forDomains) if (forDomains)
throw new SyntaxError("Nested use of domains doesn't not supported"); throw new SyntaxError("Nested use of domains doesn't not supported");
break; break;
default: 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}`); 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; return syntaxTree;
} }

View File

@ -6,12 +6,18 @@
type revHTTP; type revHTTP;
target https://github.com; target https://github.com;
}; };
domain domain.loc {
import example;
testi;
};
} }
127.0.0.1:7889 { 127.0.0.1:7889 {
import example, example_custom_auth;
type revHTTP; type revHTTP;
target https://google.com; target https://google.com;
auth loopback admin qwerty; #auth loopback admin qwerty;
cauth admin 123;
cert public ./tests/pub.crt; cert public ./tests/pub.crt;
cert private ./tests/priv.key; cert private ./tests/priv.key;
} }