!7 sclass IPInfo { S ip; Timestamp firstSeen, lastSeen, blockUntil; long passwordFails, passwordSuccesses; long blockedRequests; Bool allowed; } cm SalterService > DynPrintLogAndEnabled { !include early #1029545 // API for Eleu switchable S password = aGlobalID(); switchable int badPWBlockSeconds = 30; Map ipsSeen = syncLinkedHashMap(); start { canServe(); } // to show warnings early O html(IWebRequest req) ctex { if (!canServe()) ret "Can't serve"; S ip = req.clientIP(); IPInfo ipInfo = getOrCreate(ipsSeen, ip, () -> { new IPInfo info; info.ip = ip; info.lastSeen = new Timestamp; if (info.firstSeen == null) info.firstSeen = info.lastSeen; change(); ret info; }); if (ipInfo.blockUntil != null && cmp(ipInfo.blockUntil, new Timestamp) > 0) { print("Request from blocked IP: " + ip); ipInfo.blockedRequests++; change(); ret print("Not allowed (evil IP)"); } S uri = req.uri(); print("Serving to IP " + ip + ": " + uri); if (isFalse(ipInfo.allowed)) ret print("Not allowed (bad IP)"); S suppliedPW = req.get("password"); if (empty(suppliedPW)) ret "Need password"; if (!eq(password, suppliedPW)) { ++ipInfo.passwordFails; change(); print("Password FAIL from " + ip + " OK"); if ((ipInfo.passwordFails % 10) == 0) { infoMessage(n2(ipInfo.passwordFails) + " password fails from " + ip + "!!!!"); print("Blocking " + ip + " for " + badPWBlockSeconds + " seconds"); ipInfo.blockUntil = new Timestamp(nowPlusSeconds(badPWBlockSeconds)); change(); } } ++ipInfo.passwordSuccesses; change(); print("Password from " + ip + " OK"); ret subBot_serveJPEG(shootScreen2()); } bool canServe() { if (l(password) < 4) ret false with warn("Not starting - password too short!!"); true; } }