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);
+ }
+};