Added http mode and results
All checks were successful
Rust-build / build (push) Successful in 2m39s

This commit is contained in:
Justine Pelletreau
2023-07-22 13:48:36 +02:00
parent 5476327560
commit c4462fcbca
7 changed files with 1118 additions and 33 deletions

73
src/http.rs Normal file
View File

@ -0,0 +1,73 @@
use std::thread;
use reqwest;
use std::time::{Duration, Instant};
use std::sync::mpsc;
use crate::results::*;
fn print_results(res: Vec<StressResult>) {
let myres = ListOfResults { results: res };
myres.show_ratio();
println!("\tMean answer time : {:?}", myres.get_mean_time());
}
pub fn http_stress(url: String, interval: Duration, sleep: Duration, threads: u32) -> std::io::Result<()> {
let mut handles = vec![];
let (tx, rx) = mpsc::channel();
println!("Threads starting their {:?} run...", interval);
for i in 0..=threads {
let add = url.clone();
let end_time = Instant::now() + interval;
let wait = sleep.clone();
let tx2 = tx.clone();
handles.push(thread::spawn( move || {
loop {
if Instant::now() >= end_time { break; }
let b4 = Instant::now();
let resp = reqwest::blocking::get(add.clone());
let time = Instant::now() - b4;
match resp {
Ok(_) => {
let myres = StressResult { time: time, thread: i, ok: true };
let _ = tx2.send(myres);
//println!("Got an answer for {} in {:?} in thread {}", add, time, i);
},
Err(_) => {
let myres = StressResult { time: time, thread: i, ok: false };
let _ = tx2.send(myres);
//println!("No answer for {} after {:?} in thread {}", add, time, i);
},
}
thread::sleep(wait);
}
drop(tx2);
}));
}
drop(tx);
for handle in handles {
let _ = handle.join();
}
println!("over !");
//Showing Results
//Somehow iterating over never ends
let mut res: Vec<StressResult> = vec![];
for r in rx.try_iter() {
res.push(r);
}
print_results(res);
Ok(())
}

View File

@ -8,12 +8,18 @@ mod tcp;
use tcp::tcp_scan;
use tcp::tcp_stress;
mod http;
use http::http_stress;
mod results;
fn main() -> std::io::Result<()> {
let args = Args::parse();
let address = args.address;
let timeout = Duration::from_secs(args.timeout);
let sleep = Duration::from_millis(args.wait);
let duration = Duration::from_secs(args.duration);
let ports = handle_ports(args.ports.clone(), &args.range);
let threads = args.cthreads;
@ -29,13 +35,23 @@ fn main() -> std::io::Result<()> {
payload = "SAKAMOTO".to_string();
}
if args.stress {
tcp_stress(payload, address, timeout, sleep, ports, threads)?;
} else {
tcp_scan(address, timeout, sleep, ports)?;
match args.mode.as_str() {
"tcpstress" => {
tcp_stress(payload, address, timeout, duration, ports, threads)?;
return Ok(());
}
"tcpscan" => {
tcp_scan(address, timeout, sleep, ports)?;
return Ok(());
}
"httpstress" => {
http_stress(address, duration, sleep, threads)?;
return Ok(());
}
_ => {
panic!("Mode unkown ! See the help.");
}
}
Ok(())
}
//Returns a correct list of ports, according to range.
@ -65,25 +81,33 @@ fn print_banner() {
for line in lines {
println!("{}", line);
}
println!("");
}
///A simple TCP port scanner / stresser.
///A simple TCP/HTTP port scanner / stresser.
///If stressing, you can pass a payload via a pipe.
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
#[command(author = "Squip", version, long_about = None)]
struct Args {
///IP address or hostname to scan
///Mode of use : either tcpstress, tcpscan or httpstress
mode: String,
///IP address or hostname to scan - or url if using http
#[arg(short, long)]
address: String,
///Timeout for each connection in seconds
#[arg(short, long, default_value_t = 1)]
timeout: u64,
///Number of milliseconds to wait in between scans when scanning
///OR duration of stress when stress testing (in seconds)
///Number of milliseconds to wait in between scans or requests
#[arg(short, long, default_value_t = 30)]
wait: u64,
///Duration of tcp stress test in seconds, default 30
#[arg(short, long, default_value_t = 30)]
duration: u64,
///Ports to stress / scan, separated by commas (22,80)
#[arg(short, long, value_parser, num_args=1.., value_delimiter=',')]
#[arg(short, long, value_parser, num_args=0.., value_delimiter=',')]
ports: Vec<u32>,
///Set this flag to treat the ports as a range rather than a list
#[arg(long, short, action, default_value_t = false)]

66
src/results.rs Normal file
View File

@ -0,0 +1,66 @@
use std::time::Duration;
///Is implemented for Stress test results.
pub trait IsAResult {
///A way to return the ratio of hits to nohits (eg: "45% of our requests were answered" or
///smth)
fn show_ratio(&self);
///Return the mean hit time as a Duration
fn get_mean_time(&self) -> Duration;
}
#[derive(Debug)]
pub struct StressResult {
///How long did it take
pub time: Duration,
///What thread are we from
pub thread: u32,
///Did we get an answer
pub ok: bool,
}
pub struct ListOfResults {
pub results: Vec<StressResult>,
}
impl IsAResult for ListOfResults {
fn show_ratio(&self) {
let mut ok: u64 = 0;
let mut nok: u64 = 0;
for r in &self.results {
if r.ok == true {
*&mut ok += 1;
} else {
*&mut nok += 1;
}
}
let mut compute = ok + nok;
println!("TOTAL {}\n\tOK {}\n\tNOK {}", compute, ok, nok);
if compute < 1 { compute = 1; }
compute = ok * 100 / compute;
println!("\tGot {}% of OK", compute );
}
fn get_mean_time(&self) -> Duration {
let mut total: u128 = 0;
let mut acc: u128 = 0;
for i in &self.results {
if i.ok {
*&mut total += 1;
*&mut acc += i.time.as_millis();
}
}
if total == 0 { total += 1 };
let restime = acc / total;
Duration::from_millis(restime.try_into().unwrap())
}
}
pub fn print_results(res: Vec<StressResult>) {
let myres = ListOfResults { results: res };
myres.show_ratio();
println!("\tMean answer time : {:?}", myres.get_mean_time());
}

View File

@ -3,6 +3,9 @@ use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
use std::time::{Duration, Instant};
use std::thread;
use std::io::BufReader;
use std::sync::mpsc;
use crate::results::*;
///Scan the ports on address by attempting a tcp connection.
@ -44,16 +47,19 @@ fn resolve_add(host: &str, port: &u32) -> SocketAddr {
///Try to stress the remote machine by attempting to every given port at once and sending payload
pub fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Duration, ports: Vec<u32>, threads: u32) -> std::io::Result<()> {
let mut handles = vec![];
let (tx, rx) = mpsc::channel();
for port in ports {
tcp_scan(address.clone(), timeout, interval, vec![port])?;
tcp_scan(address.clone(), timeout, Duration::from_millis(100), vec![port])?;
let mut threads_left = threads;
let tx2 = tx.clone();
loop {
let add = address.clone();
let pay = payload.clone();
let int = interval * 1000;
let int = interval;
let end_time = Instant::now() + int;
let tx3 = tx2.clone();
//pay.push_str("\r\n");
@ -62,6 +68,8 @@ pub fn tcp_stress(payload: String, address: String, timeout: Duration, interval:
loop {
//Check if it is time to stop
if Instant::now() >= end_time { break; }
let b4 = Instant::now();
match TcpStream::connect_timeout(&socket, timeout) {
Ok(mut s) => {
//Remote port answers
@ -78,11 +86,19 @@ pub fn tcp_stress(payload: String, address: String, timeout: Duration, interval:
println!("Read {} bytes for port {} -> {}", count, &port, answer);
}
let time = Instant::now() - b4;
let myres = StressResult { time: time, thread: port.clone(), ok: true };
let _ = tx3.send(myres);
//Sleeping
thread::sleep(Duration::from_millis(10));
},
Err(_) => (),
Err(_) => {
let time = Instant::now() - b4;
let myres = StressResult { time: time, thread: port.clone(), ok: false };
let _ = tx3.send(myres);
}
}
}
}));
@ -90,10 +106,19 @@ pub fn tcp_stress(payload: String, address: String, timeout: Duration, interval:
if threads_left == 0 { break; }
}
}
println!("\nStressing {} using {} concurrent threads per port for {:?} \n", address, threads, interval * 1000);
drop(tx);
println!("\nStressing {} using {} concurrent threads per port for {:?} \n", address, threads, interval);
for handle in handles {
let _ = handle.join();
}
let mut res: Vec<StressResult> = vec![];
for r in rx.try_iter() {
res.push(r);
}
print_results(res);
Ok(())
}