From 5d32580580b1e3ad43a18a3155643c697a6fc8a0 Mon Sep 17 00:00:00 2001 From: fullgream Date: Mon, 24 Feb 2025 18:08:55 +0300 Subject: [PATCH] add new module of queries limitation control --- modules/antiddos.js | 109 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 modules/antiddos.js diff --git a/modules/antiddos.js b/modules/antiddos.js new file mode 100644 index 0000000..b633089 --- /dev/null +++ b/modules/antiddos.js @@ -0,0 +1,109 @@ +const { NetHelperModule } = global.moduleApi; +const express = require("express"); +const captcha = require("draw-captcha"); + +const HTTPServer = require("../components/_http"); + +const captchaPage = `Captcha handling required +
+

Enter captcha

+

+
+

+
+
+`; + +function randint (min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +async function getCaptchaPage (fromUrl) { + const sidDict = "1234567890abcdef"; + let sid = ""; + for (let i = 0; i < 32; i++) { + sid += sidDict[randint(0, 15)]; + } + const captchaObject = new captcha.MasterCaptcha(); + getCaptchaPage.sids[sid] = await captchaObject.setupRandomCaptcha(); + return captchaPage + .replace(new RegExp(":captchaImg:"), `/captcha/${sid}`) + .replace(new RegExp(":from:"), Buffer.from(fromUrl).toString("hex")) + .replace(new RegExp(":sid:"), sid); +} +getCaptchaPage.sids = new Object(); + +module.exports = class ExampleMdoule extends NetHelperModule { + constructor () { + super(); + this.router = express.Router(); + + this.lastUptime = new Date(new Date().getTime() + 3600000); + this.queryCount = 0; + this.ipWhiteListed = new Object(); + + this.querylimit = new this.statics.Operator("querylimit", (argv) => { + if (argv.length !== 1) return false; + return !Number.isNaN(+argv[0]); + }, (argv, self, syntaxTree) => { + if (!(syntaxTree.Type.prototype instanceof HTTPServer)) { + throw new SyntaxError("Unsupported type of server"); + } + let [ queriesLimit ] = argv; + queriesLimit = +queriesLimit; + + const router = this.router; + + router.use(async (req, res, next) => { + req.adsEx = req.adsEx ?? {}; + if (this.lastUptime <= new Date()) { + this.lastUptime = new Date(this.lastUptime.getTime() + 3600000); + this.queryCount = 1; + this.ipWhiteListed = new Object(); + } else + this.queryCount++; + if (this.queryCount > queriesLimit) { + //this.lastUptime = new Date(new Date().getTime() + 3600000); + req.adsEx.needsCaptcha = this.ipWhiteListed[req.realip] !== true; + } + return next(); + }); + + router.get("/captcha/:sid", async (req, res, next) => { + if (!req.adsEx.needsCaptcha) + return next(); + const sid = req.params.sid; + const content = getCaptchaPage.sids[sid]; + if (content === undefined) { + res.set("Content-Type", "text/plain"); + return res.status(400).send("Invalid SID. Captcha was solved or not found"); + } + res.set("Content-Type", "image/jpeg"); + res.send(content.buffer); + }); + + router.get("/captcha-solve/:sid/:redirectTo", async (req, res) => { + const sid = req.params.sid; + const content = getCaptchaPage.sids[sid]; + const redirectTo = Buffer.from(req.params.redirectTo, "hex").toString("utf8"); + const isSolved = req.query.solve?.toLowerCase() === content?.code; + this.ipWhiteListed[req.realip] = isSolved; + req.adsEx.needsCaptcha = !isSolved; + res.redirect(redirectTo); + }); + + router.use(async (req, res, next) => { + if (!req.adsEx.needsCaptcha) + return next(); + res.send(await getCaptchaPage(req.originalUrl)); + }); + }); + + this.operators.push(this.querylimit); + } + + bind (type) { + super.bind(type); + this.serverType.routelist.push(this.router); + } +};