Updates
This commit is contained in:
33
src/lib.rs
33
src/lib.rs
@@ -18,15 +18,24 @@ use tower_http::{timeout::TimeoutLayer, trace::TraceLayer};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
attack: Arc<Mutex<bool>>,
|
attack: Arc<Mutex<bool>>,
|
||||||
|
url: String,
|
||||||
|
qps: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub listen_port: u16,
|
pub listen_port: u16,
|
||||||
|
pub target_url: String,
|
||||||
|
pub qps: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(config: Config, token: CancellationToken) -> Result<(), io::Error> {
|
pub async fn run(config: Config, token: CancellationToken) -> Result<(), io::Error> {
|
||||||
|
let url = config.target_url.clone();
|
||||||
|
let qps = config.qps;
|
||||||
|
|
||||||
let state = AppState {
|
let state = AppState {
|
||||||
attack: Arc::new(Mutex::new(true)),
|
attack: Arc::new(Mutex::new(true)),
|
||||||
|
url: config.target_url,
|
||||||
|
qps: config.qps,
|
||||||
};
|
};
|
||||||
|
|
||||||
let attack = state.attack.clone();
|
let attack = state.attack.clone();
|
||||||
@@ -41,7 +50,7 @@ pub async fn run(config: Config, token: CancellationToken) -> Result<(), io::Err
|
|||||||
))
|
))
|
||||||
.with_state(state);
|
.with_state(state);
|
||||||
|
|
||||||
tokio::spawn(dos(attack));
|
tokio::spawn(dos(attack, url, qps));
|
||||||
|
|
||||||
let listener = TcpListener::bind(("0.0.0.0", config.listen_port))
|
let listener = TcpListener::bind(("0.0.0.0", config.listen_port))
|
||||||
.await
|
.await
|
||||||
@@ -63,21 +72,30 @@ async fn stop(State(state): State<AppState>) -> impl IntoResponse {
|
|||||||
|
|
||||||
async fn reset(State(state): State<AppState>) -> impl IntoResponse {
|
async fn reset(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
let mut attack = state.attack.lock().await;
|
let mut attack = state.attack.lock().await;
|
||||||
|
let mut reset_attack = false;
|
||||||
|
if !*attack {
|
||||||
|
reset_attack = true;
|
||||||
|
}
|
||||||
*attack = true;
|
*attack = true;
|
||||||
|
if reset_attack {
|
||||||
|
tokio::spawn(dos(state.attack.clone(), state.url, state.qps));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn index_page(State(state): State<AppState>) -> impl IntoResponse {
|
async fn index_page(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
let attack = state.attack.lock().await.clone();
|
let attack = state.attack.lock().await.clone();
|
||||||
let template = IndexTemplate { attack };
|
let template = IndexTemplate {
|
||||||
|
attack,
|
||||||
|
url: state.url,
|
||||||
|
qps: state.qps,
|
||||||
|
};
|
||||||
HtmlTemplate(template)
|
HtmlTemplate(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn dos(attack: Arc<Mutex<bool>>) {
|
async fn dos(attack: Arc<Mutex<bool>>, url: String, qps: usize) {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
let url = "https://pms.szamtech.ktk.bme.hu/en/login";
|
let state = TokenBucketState::new(qps, qps / 10, Duration::from_millis(100));
|
||||||
|
|
||||||
let state = TokenBucketState::new(100, 10, Duration::from_millis(100));
|
|
||||||
let state_mutex = Arc::new(Mutex::new(state));
|
let state_mutex = Arc::new(Mutex::new(state));
|
||||||
let mut rl = TokenBucketRateLimiter::new(state_mutex);
|
let mut rl = TokenBucketRateLimiter::new(state_mutex);
|
||||||
|
|
||||||
@@ -85,6 +103,7 @@ async fn dos(attack: Arc<Mutex<bool>>) {
|
|||||||
if let false = attack.lock().await.clone() {
|
if let false = attack.lock().await.clone() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let url = url.clone();
|
||||||
rl.wait_with_cost(1).await;
|
rl.wait_with_cost(1).await;
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -97,6 +116,8 @@ async fn dos(attack: Arc<Mutex<bool>>) {
|
|||||||
#[template(path = "index.html")]
|
#[template(path = "index.html")]
|
||||||
struct IndexTemplate {
|
struct IndexTemplate {
|
||||||
attack: bool,
|
attack: bool,
|
||||||
|
url: String,
|
||||||
|
qps: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HtmlTemplate<T>(T);
|
struct HtmlTemplate<T>(T);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
use aoszdos::Config;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{env, fmt::Display};
|
use std::{env, fmt::Display};
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
use tokio::signal::unix::{signal, SignalKind};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||||
use aoszdos::Config;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), String> {
|
async fn main() -> Result<(), String> {
|
||||||
@@ -32,6 +32,8 @@ async fn main() -> Result<(), String> {
|
|||||||
// Parse environment variables and create a Config struct
|
// Parse environment variables and create a Config struct
|
||||||
let config = Config {
|
let config = Config {
|
||||||
listen_port: parse_env("LISTEN_PORT", 8080),
|
listen_port: parse_env("LISTEN_PORT", 8080),
|
||||||
|
target_url: parse_env("TARGET_URL", String::from("localhost")),
|
||||||
|
qps: parse_env("TARGET_QPS", 100),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (Ok(mut sigterm), Ok(mut sigint)) = (
|
let (Ok(mut sigterm), Ok(mut sigint)) = (
|
||||||
|
|||||||
@@ -1,66 +1,190 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>Attack Status</title>
|
<title>Attack Status — Advent of Szamtech</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
margin: 0;
|
||||||
text-align: center;
|
height: 100vh;
|
||||||
margin-top: 100px;
|
display: flex;
|
||||||
transition: background-color 0.3s;
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: "Segoe UI", Tahoma, sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 6px rgba(255, 255, 255, 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
transition: background-color 0.4s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Cyber snowfall */
|
||||||
|
body::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background-image: radial-gradient(#0f0 1px, transparent 1px),
|
||||||
|
radial-gradient(#0f0 1px, transparent 1px);
|
||||||
|
background-size:
|
||||||
|
20px 20px,
|
||||||
|
25px 25px;
|
||||||
|
opacity: 0.07;
|
||||||
|
animation: snowfall 15s linear infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
@keyframes snowfall {
|
||||||
|
from {
|
||||||
|
transform: translateY(-100px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(100px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CRT scanlines */
|
||||||
|
body::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(255, 255, 255, 0.03) 0px,
|
||||||
|
rgba(255, 255, 255, 0.03) 2px,
|
||||||
|
transparent 3px,
|
||||||
|
transparent 4px
|
||||||
|
);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main status text */
|
||||||
#message {
|
#message {
|
||||||
font-size: 2rem;
|
font-size: 2.4rem;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 20px;
|
||||||
|
animation: glowPulse 3s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Emergency STOP button */
|
@keyframes glowPulse {
|
||||||
#stopBtn {
|
0% {
|
||||||
width: 180px;
|
text-shadow:
|
||||||
height: 180px;
|
0 0 4px #fff,
|
||||||
border-radius: 50%;
|
0 0 12px #4fff4f;
|
||||||
background: radial-gradient(circle at 30% 30%, #ff6b6b, #b30000);
|
}
|
||||||
border: 12px solid #f1c40f; /* yellow ring */
|
50% {
|
||||||
box-shadow:
|
text-shadow:
|
||||||
0 8px 0 #8c0000, /* top shadow */
|
0 0 10px #7dff7d,
|
||||||
0 15px 20px rgba(0,0,0,0.5);
|
0 0 20px #00ff00;
|
||||||
color: white;
|
}
|
||||||
font-size: 2.2rem;
|
100% {
|
||||||
font-weight: bold;
|
text-shadow:
|
||||||
cursor: pointer;
|
0 0 4px #fff,
|
||||||
outline: none;
|
0 0 12px #4fff4f;
|
||||||
transition: all 0.1s ease-in-out;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alert shimmer */
|
||||||
|
.alertShimmer {
|
||||||
|
animation: alertGlow 1.2s infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes alertGlow {
|
||||||
|
from {
|
||||||
|
text-shadow:
|
||||||
|
0 0 6px #ff0000,
|
||||||
|
0 0 20px #ff2020;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
text-shadow:
|
||||||
|
0 0 12px #ff4d4d,
|
||||||
|
0 0 35px #ff0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INFO PANEL under the attack text */
|
||||||
|
#infoPanel {
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: center;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pressed effect */
|
#targetLine,
|
||||||
#stopBtn:active {
|
#qpsLine {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: #7dff7d;
|
||||||
|
text-shadow: 0 0 8px #00ff99;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STOP button */
|
||||||
|
#stopBtn {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 10px solid rgba(255, 215, 0, 0.8);
|
||||||
|
background: radial-gradient(circle at 30% 30%, #ff7b7b, #990000);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 3px 0 #8c0000,
|
0 0 20px #ff2e2e,
|
||||||
0 5px 10px rgba(0,0,0,0.4);
|
0 0 50px #ff0000,
|
||||||
transform: translateY(5px);
|
0 10px 20px rgba(0, 0, 0, 0.6);
|
||||||
|
font-size: 2.4rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
transition: all 0.15s ease-in-out;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stopBtn::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 50px;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#stopBtn:active {
|
||||||
|
transform: translateY(6px) scale(0.96);
|
||||||
|
box-shadow:
|
||||||
|
0 0 15px #ff2e2e,
|
||||||
|
0 5px 10px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
|
|
||||||
|
<!-- Info Panel -->
|
||||||
|
<div id="infoPanel">
|
||||||
|
<div id="targetLine">Target: <strong id="targetSpan"></strong></div>
|
||||||
|
<div id="qpsLine">QPS: <strong id="qpsSpan"></strong></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="stopBtn">STOP</button>
|
<button id="stopBtn">STOP</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Server-side injected value
|
|
||||||
const attack = {{ attack }};
|
const attack = {{ attack }};
|
||||||
|
const target = "{{ url }}";
|
||||||
|
const qps = "{{ qps }}";
|
||||||
|
|
||||||
const message = document.getElementById("message");
|
const message = document.getElementById("message");
|
||||||
const stopBtn = document.getElementById("stopBtn");
|
const stopBtn = document.getElementById("stopBtn");
|
||||||
|
|
||||||
|
const infoPanel = document.getElementById("infoPanel");
|
||||||
|
const targetSpan = document.getElementById("targetSpan");
|
||||||
|
const qpsSpan = document.getElementById("qpsSpan");
|
||||||
|
|
||||||
const green = () => {
|
const green = () => {
|
||||||
document.body.style.backgroundColor = "#7dff7d"; // green background
|
document.body.style.backgroundColor = "#004d00";
|
||||||
message.textContent = "✅ The attack has been stopped.";
|
message.innerHTML = "🎄✨ <span style='color:#7dff7d'>Sikeresen megallitottatok Gr(F)inch-et</span> ✨🎄";
|
||||||
|
message.classList.remove("alertShimmer");
|
||||||
stopBtn.style.display = "none";
|
stopBtn.style.display = "none";
|
||||||
}
|
infoPanel.style.display = "none";
|
||||||
|
};
|
||||||
|
|
||||||
stopBtn.onclick = async () => {
|
stopBtn.onclick = async () => {
|
||||||
await fetch("/stop");
|
await fetch("/stop");
|
||||||
@@ -68,13 +192,19 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (attack) {
|
if (attack) {
|
||||||
document.body.style.backgroundColor = "#ff4d4d"; // red background
|
document.body.style.backgroundColor = "#3b0000";
|
||||||
message.textContent = "⚠️ Attack in progress...";
|
message.textContent = "⚠️ Gr(F)inch anti-Karacsony DoS tamadas aktiv... ⚠️";
|
||||||
|
message.classList.add("alertShimmer");
|
||||||
|
|
||||||
|
// print target & qps
|
||||||
|
targetSpan.textContent = target;
|
||||||
|
qpsSpan.textContent = qps;
|
||||||
|
|
||||||
|
infoPanel.style.display = "block";
|
||||||
stopBtn.style.display = "inline-block";
|
stopBtn.style.display = "inline-block";
|
||||||
} else {
|
} else {
|
||||||
green();
|
green();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user