Added range feature and and dns resolving
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		@ -1,6 +1,6 @@
 | 
				
			|||||||
[package]
 | 
					[package]
 | 
				
			||||||
name = "portnut"
 | 
					name = "portnut"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.1"
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
				
			|||||||
@ -34,16 +34,17 @@ Stressing 127.0.0.1 using 100 concurrent threads per port for 30s
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
See the help :
 | 
					See the help :
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
1648 justine@portnut > target/release/portnut -h
 | 
					justine@portnut > target/release/portnut -h
 | 
				
			||||||
A simple TCP port scanner / stresser. If stressing, you can pass a payload via a pipe
 | 
					A simple TCP port scanner / stresser. If stressing, you can pass a payload via a pipe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Usage: portnut [OPTIONS] --address <ADDRESS>
 | 
					Usage: portnut [OPTIONS] --address <ADDRESS>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Options:
 | 
					Options:
 | 
				
			||||||
  -a, --address <ADDRESS>    IP address to scan
 | 
					  -a, --address <ADDRESS>    IP address or hostname to scan
 | 
				
			||||||
  -t, --timeout <TIMEOUT>    Timeout for each connection in seconds [default: 1]
 | 
					  -t, --timeout <TIMEOUT>    Timeout for each connection in seconds [default: 1]
 | 
				
			||||||
  -w, --wait <WAIT>          Number of milliseconds to wait in between scans when scanning OR duration of stress when stress testing (in seconds) [default: 30]
 | 
					  -w, --wait <WAIT>          Number of milliseconds to wait in between scans when scanning OR duration of stress when stress testing (in seconds) [default: 30]
 | 
				
			||||||
  -p, --ports <PORTS>...     Ports to stress / scan, separated by commas (22,80)
 | 
					  -p, --ports <PORTS>...     Ports to stress / scan, separated by commas (22,80)
 | 
				
			||||||
 | 
					  -r, --range                Set this flag to treat the ports as a range rather than a list
 | 
				
			||||||
  -s, --stress               Set this flag to stress the ports instead of scanning them
 | 
					  -s, --stress               Set this flag to stress the ports instead of scanning them
 | 
				
			||||||
  -c, --cthreads <CTHREADS>  How many threads per port when stressing (Concurrent Threads) [default: 5]
 | 
					  -c, --cthreads <CTHREADS>  How many threads per port when stressing (Concurrent Threads) [default: 5]
 | 
				
			||||||
  -h, --help                 Print help
 | 
					  -h, --help                 Print help
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										69
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								src/main.rs
									
									
									
									
									
								
							@ -1,10 +1,11 @@
 | 
				
			|||||||
use clap::Parser;
 | 
					use clap::Parser;
 | 
				
			||||||
use std::io::prelude::*;
 | 
					use std::io::prelude::*;
 | 
				
			||||||
use std::io::{self, Read};
 | 
					use std::io::{self, Read};
 | 
				
			||||||
use std::net::{SocketAddr, TcpStream};
 | 
					use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
 | 
				
			||||||
use std::time::{Duration, Instant};
 | 
					use std::time::{Duration, Instant};
 | 
				
			||||||
use std::thread;
 | 
					use std::thread;
 | 
				
			||||||
use atty::Stream;
 | 
					use atty::Stream;
 | 
				
			||||||
 | 
					use std::io::BufReader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() -> std::io::Result<()> {
 | 
					fn main() -> std::io::Result<()> {
 | 
				
			||||||
@ -12,7 +13,7 @@ fn main() -> std::io::Result<()> {
 | 
				
			|||||||
    let address = args.address;
 | 
					    let address = args.address;
 | 
				
			||||||
    let timeout = Duration::from_secs(args.timeout);
 | 
					    let timeout = Duration::from_secs(args.timeout);
 | 
				
			||||||
    let sleep = Duration::from_millis(args.wait);
 | 
					    let sleep = Duration::from_millis(args.wait);
 | 
				
			||||||
    let ports = args.ports;
 | 
					    let ports = handle_ports(args.ports.clone(), &args.range);
 | 
				
			||||||
    let threads = args.cthreads;
 | 
					    let threads = args.cthreads;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    //Reading the payload from stdin (if applicable)
 | 
					    //Reading the payload from stdin (if applicable)
 | 
				
			||||||
@ -34,17 +35,33 @@ fn main() -> std::io::Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//Returns a correct list of ports, according to range.
 | 
				
			||||||
 | 
					//If range is true, we send a range of ports. Otherwise
 | 
				
			||||||
 | 
					//we send the original back
 | 
				
			||||||
 | 
					fn handle_ports(ports: Vec<u32>, range: &bool) -> Vec<u32> {
 | 
				
			||||||
 | 
					    if *range {
 | 
				
			||||||
 | 
					        if ports.iter().count() != 2 {
 | 
				
			||||||
 | 
					            panic!("A range must be given only a start and an end value.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let ret: Vec<u32> = (ports[0]..ports[1]).collect();
 | 
				
			||||||
 | 
					        ret
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        ports
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///Scan the ports on address by attempting a tcp connection.
 | 
					///Scan the ports on address by attempting a tcp connection.
 | 
				
			||||||
fn tcp_scan(address: String, timeout: Duration, sleep: Duration, ports: Vec<u32>) -> std::io::Result<()> {
 | 
					fn tcp_scan(address: String, timeout: Duration, sleep: Duration, ports: Vec<u32>) -> std::io::Result<()> {
 | 
				
			||||||
    println!("Scanning {}", address);
 | 
					    println!("Scanning {}", address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for i in ports {
 | 
					    for i in ports {
 | 
				
			||||||
        let socket = format!("{}:{}", &address, i);
 | 
					        let socket = resolve_add(&address.as_str(), &i);
 | 
				
			||||||
        let socket = socket.parse::<SocketAddr>().unwrap();
 | 
					 | 
				
			||||||
        match TcpStream::connect_timeout(&socket, timeout) {
 | 
					        match TcpStream::connect_timeout(&socket, timeout) {
 | 
				
			||||||
            Ok(mut s) => {
 | 
					            Ok(mut _s) => {
 | 
				
			||||||
                println!("TCP/{} => ACCEPT", i);
 | 
					                println!("TCP/{} => ACCEPT", i);
 | 
				
			||||||
                s.write(b"Some padding")?;
 | 
					                //s.write(b"Some padding")?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Err(_) => {
 | 
					            Err(_) => {
 | 
				
			||||||
                println!("TCP/{} => REJECT", i);
 | 
					                println!("TCP/{} => REJECT", i);
 | 
				
			||||||
@ -58,8 +75,18 @@ fn tcp_scan(address: String, timeout: Duration, sleep: Duration, ports: Vec<u32>
 | 
				
			|||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///Try to stress the remote machine by attempting to every given port at once and sending random
 | 
					///Takes either an hostname or an IP address and port, and resolve to a socket address.
 | 
				
			||||||
///data
 | 
					///DNS recordings can lead to multiple entries : we take the first one.
 | 
				
			||||||
 | 
					fn resolve_add(host: &str, port: &u32) -> SocketAddr {
 | 
				
			||||||
 | 
					    let details = format!("{}:{}", host, port);
 | 
				
			||||||
 | 
					    let server: Vec<_> = details
 | 
				
			||||||
 | 
					        .to_socket_addrs()
 | 
				
			||||||
 | 
					        .expect("Can't resolve your address")
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					    return server[0];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					///Try to stress the remote machine by attempting to every given port at once and sending payload
 | 
				
			||||||
fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Duration, ports: Vec<u32>, threads: u32) -> std::io::Result<()> {
 | 
					fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Duration, ports: Vec<u32>, threads: u32) -> std::io::Result<()> {
 | 
				
			||||||
    let mut handles = vec![];
 | 
					    let mut handles = vec![];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -72,20 +99,33 @@ fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Dur
 | 
				
			|||||||
            let pay = payload.clone();
 | 
					            let pay = payload.clone();
 | 
				
			||||||
            let int = interval * 1000;
 | 
					            let int = interval * 1000;
 | 
				
			||||||
            let end_time = Instant::now() + int;
 | 
					            let end_time = Instant::now() + int;
 | 
				
			||||||
 | 
					            //pay.push_str("\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            handles.push(thread::spawn( move || {
 | 
					            handles.push(thread::spawn( move || {
 | 
				
			||||||
                let socket = format!("{}:{}", &add, &port);
 | 
					                let socket = resolve_add(&add.as_str(), &port);
 | 
				
			||||||
                let socket = socket.parse::<SocketAddr>().unwrap();
 | 
					 | 
				
			||||||
                loop {
 | 
					                loop {
 | 
				
			||||||
                    //Check if it is time to stop
 | 
					                    //Check if it is time to stop
 | 
				
			||||||
                    if Instant::now() >= end_time { break; }
 | 
					                    if Instant::now() >= end_time { break; }
 | 
				
			||||||
                    match TcpStream::connect_timeout(&socket, timeout) {
 | 
					                    match TcpStream::connect_timeout(&socket, timeout) {
 | 
				
			||||||
                        Ok(mut s) => {
 | 
					                        Ok(mut s) => {
 | 
				
			||||||
                            s.write(pay.as_bytes()).unwrap();
 | 
					                            //Remote port answers
 | 
				
			||||||
 | 
					                            //Writing
 | 
				
			||||||
 | 
					                            s.write_all(pay.as_bytes()).unwrap();
 | 
				
			||||||
                            s.flush().unwrap();
 | 
					                            s.flush().unwrap();
 | 
				
			||||||
                            //s.read(&mut [0; 128]).unwrap();
 | 
					
 | 
				
			||||||
 | 
					                            //Reading
 | 
				
			||||||
 | 
					                            s.set_read_timeout(Some(Duration::from_millis(500))).unwrap();
 | 
				
			||||||
 | 
					                            let mut reader = BufReader::new(s.try_clone().unwrap());
 | 
				
			||||||
 | 
					                            let mut answer = String::new();
 | 
				
			||||||
 | 
					                            let count = reader.read_line(&mut answer).unwrap_or(0);
 | 
				
			||||||
 | 
					                            if count > 0 {
 | 
				
			||||||
 | 
					                                println!("Read {} bytes for port {} -> {}", count, &port, answer);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            //Sleeping
 | 
				
			||||||
                            thread::sleep(Duration::from_millis(10));
 | 
					                            thread::sleep(Duration::from_millis(10));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        Err(_) => (),
 | 
					                        Err(_) => (),
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -107,7 +147,7 @@ fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Dur
 | 
				
			|||||||
#[derive(Debug, Parser)]
 | 
					#[derive(Debug, Parser)]
 | 
				
			||||||
#[command(author, version, about, long_about = None)]
 | 
					#[command(author, version, about, long_about = None)]
 | 
				
			||||||
struct Args {
 | 
					struct Args {
 | 
				
			||||||
    ///IP address to scan
 | 
					    ///IP address or hostname to scan
 | 
				
			||||||
    #[arg(short, long)]
 | 
					    #[arg(short, long)]
 | 
				
			||||||
    address: String,
 | 
					    address: String,
 | 
				
			||||||
    ///Timeout for each connection in seconds
 | 
					    ///Timeout for each connection in seconds
 | 
				
			||||||
@ -120,6 +160,9 @@ struct Args {
 | 
				
			|||||||
    ///Ports to stress / scan, separated by commas (22,80)
 | 
					    ///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=1.., value_delimiter=',')]
 | 
				
			||||||
    ports: Vec<u32>,
 | 
					    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)]
 | 
				
			||||||
 | 
					    range: bool,
 | 
				
			||||||
    ///Set this flag to stress the ports instead of scanning them
 | 
					    ///Set this flag to stress the ports instead of scanning them
 | 
				
			||||||
    #[arg(long, short, action, default_value_t = false)]
 | 
					    #[arg(long, short, action, default_value_t = false)]
 | 
				
			||||||
    stress: bool,
 | 
					    stress: bool,
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user