mirror of
https://github.com/bee-san/RustScan.git
synced 2026-11-08 01:08:27 -10:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1639aadc54 | ||
|
|
c66bb50508 | ||
|
|
b490eb08be | ||
|
|
990e92dcbf | ||
|
|
883e89fe5e | ||
|
|
1b15cc2f71 | ||
|
|
18b574f6bb | ||
|
|
3fafee42b9 | ||
|
|
8b21dfd9a2 | ||
|
|
2f16729c54 | ||
|
|
785c2a4de1 | ||
|
|
80cc97bf83 | ||
|
|
7a825dae41 | ||
|
|
751c4daff6 | ||
|
|
f6214cfb27 | ||
|
|
a92eb80365 | ||
|
|
aeab4fb68c |
No files matched your search
Generated
+7
-7
@@ -165,9 +165,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.1"
|
||||
version = "2.33.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||
checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0",
|
||||
"atty",
|
||||
@@ -197,9 +197,9 @@ checksum = "0bca1619ff57dd7a56b58a8e25ef4199f123e78e503fe1653410350a1b98ae65"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ff9c66992ebea984b9446b2a45b300947216c17544ec9d7f045afe24333cd0"
|
||||
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
|
||||
dependencies = [
|
||||
"cache-padded",
|
||||
]
|
||||
@@ -256,9 +256,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "298f00c3b04c1d9b4cb86aefaaa35348af0957d98b30a5306fc635f8e718923d"
|
||||
checksum = "68082183f458867ce7cddea16d4df4443c1537112c0c09c450dedc09daf5c719"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
@@ -685,7 +685,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustscan"
|
||||
version = "1.6.0"
|
||||
version = "1.7.1"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"async-std",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustscan"
|
||||
version = "1.6.0"
|
||||
version = "1.7.1"
|
||||
authors = ["Bee <[email protected]>"]
|
||||
edition = "2018"
|
||||
description = "Faster Nmap Scanning with Rust"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
➡️
|
||||
➡️
|
||||
<a href="https://discord.gg/GFrQsGy">Discord</a> |
|
||||
<a href="https://github.com/RustScan/RustScan#-full-installation-guide">Installation Guide</a> |
|
||||
<a href="https://github.com/RustScan/RustScan#-usage">Usage Guide</a>
|
||||
@@ -8,7 +8,7 @@
|
||||
<img src="pictures/rustscan.png">
|
||||
</p>
|
||||
<p align="center">
|
||||
<u><b> Turns a 17 minutes Nmap scan into 19 seconds. </b></u><br> Find all open ports <b>fast</b> with RustScan, automatically pipe them into Nmap.
|
||||
<u><b> Turns a 17 minutes Nmap scan into 19 seconds. </b></u><br> Find all open ports <b>fast</b> with RustScan, automatically pipe them into Nmap.
|
||||
</p>
|
||||
<p align="center">
|
||||
<img alt="AUR version" src="https://img.shields.io/aur/version/rustscan-bin">
|
||||
@@ -98,7 +98,7 @@ The easiest way to install RustScan is to use one of the packages provided for y
|
||||
|
||||
The most universal way is to use `cargo`, Rust's built in package manager (think Pip but for Rust). [Follow this guide to installing Rust & Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html).
|
||||
|
||||
If you face any issues at all, please leave a GitHub issue. I have only tested this on Linux, so there may be issues for Mac OS or Windows.
|
||||
If you face any issues at all, please leave a GitHub issue. I have only tested this on Linux, so there may be issues for Mac OS or Windows.
|
||||
|
||||
Note: sometimes Rust doesn't add Cargo to the path. Please see [this issue](https://github.com/rust-lang/rustup/issues/2436) for how to fix that.
|
||||
|
||||
@@ -108,7 +108,7 @@ Download the .deb file from the releases page:
|
||||
|
||||
[https://github.com/brandonskerritt/RustScan/releases](https://github.com/brandonskerritt/RustScan/releases)
|
||||
|
||||
Run the commpand `dpkg -i` on the file.
|
||||
Run the commpand `dpkg -i` on the file.
|
||||
|
||||
Note: sometimes you can double click the file to achieve the same result.
|
||||
|
||||
@@ -130,7 +130,7 @@ docker run -it --rm --name rustscan cmnatic/rustscan:debian-buster rustscan 127.
|
||||
|
||||
Note: this will scan the Docker's localhost, not your own.
|
||||
|
||||
This will download the Docker image.
|
||||
This will download the Docker image.
|
||||
|
||||
Once done, you will no longer need to re-download the image (except when RustScan updates) and can use RustScan like a normal application.
|
||||
|
||||
@@ -186,43 +186,47 @@ rustscan -h
|
||||
```
|
||||
|
||||
```
|
||||
RustScan 1.2.0
|
||||
Bee https://github.com/brandonskerritt
|
||||
Fast Port Scanner built in Rust
|
||||
WARNING Do not use this program against sensitive infrastructure. The specified server may not be able to handle this
|
||||
many socket connections at once.
|
||||
rustscan 1.7.1
|
||||
Fast Port Scanner built in Rust. WARNING Do not use this program against sensitive infrastructure since the specified
|
||||
server may not be able to handle this many socket connections at once. - Discord https://discord.gg/GFrQsGy - GitHub
|
||||
https://github.com/RustScan/RustScan
|
||||
|
||||
USAGE:
|
||||
rustscan [FLAGS] [OPTIONS] <ip> [command]...
|
||||
rustscan [FLAGS] [OPTIONS] <ips>... [-- <command>...]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-q, --quiet Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
|
||||
-V, --version Prints version information
|
||||
-a, --accessible
|
||||
-h, --help Prints help information
|
||||
-q, --quiet Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-T, --timeout <T> The timeout before a port is assumed to be close. In MS. [default: 1500]
|
||||
-b, --batch <b> Increases speed of scanning. The batch size for port scanning. Depends on your open file limit
|
||||
of OS. If you do 65535 it will do every port at the same time. Although, your OS may not
|
||||
support this. [default: 4500]
|
||||
-u, --ulimit <u> Automatically ups the ULIMIT with the value you provided.
|
||||
-b, --batch-size <batch-size> The batch size for port scanning, it increases or slows the speed of scanning.
|
||||
Depends on the open file limit of your OS. If you do 65535 it will do every port
|
||||
at the same time. Although, your OS may not support this [default: 4500]
|
||||
--scan-order <scan-order> The order of scanning to be performed. The "serial" option will scan ports in
|
||||
ascending order while the "random" option will scan ports randomly [default:
|
||||
serial] [possible values: Serial, Random]
|
||||
-t, --timeout <timeout> The timeout in milliseconds before a port is assumed to be closed [default: 1500]
|
||||
-u, --ulimit <ulimit> Automatically ups the ULIMIT with the value you provided
|
||||
|
||||
ARGS:
|
||||
<ip> The IP address to scan
|
||||
<command>... The Nmap arguments to run. To use the argument -A, end RustScan's args with '-- -A'. To run
|
||||
EXAMPLE: 'rustscan -T 1500 127.0.0.1 -- -A -sC'. This argument auto runs nmap {your commands}
|
||||
-vvv -p $PORTS
|
||||
<ips>... A list of comma separated IP addresses to be scanned
|
||||
<command>... The Nmap arguments to run. To use the argument -A, end RustScan's args with '-- -A'. Example:
|
||||
'rustscan -T 1500 127.0.0.1 -- -A -sC'. This command adds -Pn -vvv -p $PORTS automatically to
|
||||
nmap. For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and
|
||||
vuln)'\"")
|
||||
```
|
||||
|
||||
The format is `rustscan -b 500 -T 1500 192.168.0.1` to scan 192.168.0.1 with 500 batch size with a timeout of 1500ms. The timeout is how long RustScan waits for a response until it assumes the port is closed.
|
||||
|
||||
The batch size determines how fast RustScan is. Set it to 65k, and it will scan all 65k ports at the same time. This means at at 65k batch size, RustScan will take TIMEOUT long to scan all ports. Essentially, if timeout is 1000ms, **RustScan can scan in 1 second**.
|
||||
The batch size determines how fast RustScan is. Set it to 65k, and it will scan all 65k ports at the same time. This means at at 65k batch size, RustScan will take TIMEOUT long to scan all ports. Essentially, if timeout is 1000ms, **RustScan can scan in 1 second**.
|
||||
|
||||
Your operating system may not support this, but it is worth it to play around and see where your open file limit is. Shortly I will be releasing a dockerised version with a much larger open file limit, so this will be possible.
|
||||
|
||||
## ⚠️ WARNING
|
||||
|
||||
This program, by default, scans 5000 ports at a time (5000 per second).
|
||||
This program, by default, scans 5000 ports at a time (5000 per second).
|
||||
|
||||
This may cause damage to a server, or may make it incredibly obvious you are scanning the server.
|
||||
|
||||
@@ -245,7 +249,7 @@ This limit changes from OS to OS.
|
||||
|
||||
RustScan does not automatically create defaults (other than 5000) like Nmap does with their -T1, -T2 system.
|
||||
|
||||
By figuring out for yourself the optimal batch size, you will know that RustScan is the most optimised port scanner for your system.
|
||||
By figuring out for yourself the optimal batch size, you will know that RustScan is the most optimised port scanner for your system.
|
||||
|
||||
There are 2 things you can do:
|
||||
1. Decrease batch size
|
||||
@@ -274,16 +278,16 @@ ulimit -n 5000
|
||||
```
|
||||
|
||||
**Mac OS**
|
||||
Mac OS has, from what I can tell, a naturally very low open file descriptor limit. The limit for Ubuntu is 8800. The limit for Mac OS is 255!
|
||||
Mac OS has, from what I can tell, a naturally very low open file descriptor limit. The limit for Ubuntu is 8800. The limit for Mac OS is 255!
|
||||
|
||||
In this case, I would say it is safe to increase the open file limit. As most Linux based OS' have limits in the thousands.
|
||||
In this case, I would say it is safe to increase the open file limit. As most Linux based OS' have limits in the thousands.
|
||||
|
||||
Although, if this breaks anything, please don't blame me.
|
||||
Although, if this breaks anything, please don't blame me.
|
||||
|
||||
**Windows Subsystem for Linux**
|
||||
Windows Subsystem for Linux does not support ulimt (see issue #39).
|
||||
Windows Subsystem for Linux does not support ulimit (see issue #39).
|
||||
|
||||
The best way is to use it on a host computer, in Docker, or in a VM that isn't WSL.
|
||||
The best way is to use it on a host computer, in Docker, or in a VM that isn't WSL.
|
||||
|
||||
**Automatic Ulimit updating**
|
||||
We are currently working on automatic Ulimit updating. If it is too high, it will lower itself. If it is too low, it will suggest a higher Ulimit. Watch [this issue](https://github.com/brandonskerritt/RustScan/issues/25) for more.
|
||||
@@ -291,7 +295,7 @@ We are currently working on automatic Ulimit updating. If it is too high, it wil
|
||||
## 🔌 Nmap Custom Flags
|
||||
To run your own nmap commands, end the RustScan command with `-- -A` where `--` indicates "end of RustScan flags, please do not parse anything further" and any flags after that will be entered into nmap.
|
||||
|
||||
RustScan automatically runs `nmap -vvv -p $PORTS $IP`. To make it run `-A`, execute the command `rustscan 127.0.0.1 -- -A`.
|
||||
RustScan automatically runs `nmap -vvv -p $PORTS $IP`. To make it run `-A`, execute the command `rustscan 127.0.0.1 -- -A`.
|
||||
|
||||
If you want to run commands such as `--script (vuln and safe)`, you will need to enclose it in quotations like so `--script '"(vuln and safe) or default"'`.
|
||||
|
||||
@@ -320,7 +324,7 @@ TL;DR if you abuse members of our community you will be **perma-banned** 🤗
|
||||
|
||||
RustScan has 2 major labels for GitHub issues you should look at:
|
||||
* Good First issue
|
||||
These are issues for newcomers to open source!
|
||||
These are issues for newcomers to open source!
|
||||
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
|
||||
* Help wanted
|
||||
These are issues that aren't really for newcomers, but we could still do wiht help!
|
||||
|
||||
+37
-10
@@ -5,6 +5,9 @@ mod tui;
|
||||
mod scanner;
|
||||
use scanner::Scanner;
|
||||
|
||||
mod port_strategy;
|
||||
use port_strategy::PortStrategy;
|
||||
|
||||
use colorful::Color;
|
||||
use colorful::Colorful;
|
||||
use futures::executor::block_on;
|
||||
@@ -13,7 +16,7 @@ use rlimit::{getrlimit, setrlimit};
|
||||
use std::collections::HashMap;
|
||||
use std::process::Command;
|
||||
use std::{net::IpAddr, time::Duration};
|
||||
use structopt::StructOpt;
|
||||
use structopt::{clap::arg_enum, StructOpt};
|
||||
|
||||
extern crate colorful;
|
||||
extern crate dirs;
|
||||
@@ -28,6 +31,14 @@ const AVERAGE_BATCH_SIZE: rlimit::rlim = 3000;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
arg_enum! {
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum ScanOrder {
|
||||
Serial,
|
||||
Random,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
|
||||
/// Fast Port Scanner built in Rust.
|
||||
@@ -44,6 +55,10 @@ struct Opts {
|
||||
#[structopt(short, long)]
|
||||
quiet: bool,
|
||||
|
||||
//Accessible mode. Turns off features which negatively affect screen readers.
|
||||
#[structopt(short, long)]
|
||||
accessible: bool,
|
||||
|
||||
/// The batch size for port scanning, it increases or slows the speed of
|
||||
/// scanning. Depends on the open file limit of your OS. If you do 65535
|
||||
/// it will do every port at the same time. Although, your OS may not
|
||||
@@ -59,6 +74,12 @@ struct Opts {
|
||||
#[structopt(short, long)]
|
||||
ulimit: Option<rlimit::rlim>,
|
||||
|
||||
/// The order of scanning to be performed. The "serial" option will
|
||||
/// scan ports in ascending order while the "random" option will scan
|
||||
/// ports randomly.
|
||||
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
|
||||
scan_order: ScanOrder,
|
||||
|
||||
/// The Nmap arguments to run.
|
||||
/// To use the argument -A, end RustScan's args with '-- -A'.
|
||||
/// Example: 'rustscan -T 1500 127.0.0.1 -- -A -sC'.
|
||||
@@ -78,7 +99,7 @@ fn main() {
|
||||
let opts = Opts::from_args();
|
||||
info!("Mains() `opts` arguments are {:?}", opts);
|
||||
|
||||
if !opts.quiet {
|
||||
if !opts.quiet && !opts.accessible {
|
||||
print_opening();
|
||||
}
|
||||
|
||||
@@ -87,11 +108,10 @@ fn main() {
|
||||
|
||||
let scanner = Scanner::new(
|
||||
&opts.ips,
|
||||
LOWEST_PORT_NUMBER,
|
||||
TOP_PORT_NUMBER,
|
||||
batch_size,
|
||||
Duration::from_millis(opts.timeout.into()),
|
||||
opts.quiet,
|
||||
PortStrategy::pick(LOWEST_PORT_NUMBER, TOP_PORT_NUMBER, opts.scan_order),
|
||||
);
|
||||
|
||||
let scan_result = block_on(scanner.run());
|
||||
@@ -234,10 +254,7 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
|
||||
|
||||
// Adjust the batch size when the ulimit value is lower than the desired batch size
|
||||
if ulimit < batch_size {
|
||||
warning!(
|
||||
"File limit is lower than default batch size.
|
||||
Consider upping with --ulimt.
|
||||
May cause harm to sensitive servers",
|
||||
warning!("File limit is lower than default batch size. Consider upping with --ulimt. May cause harm to sensitive servers",
|
||||
opts.quiet
|
||||
);
|
||||
|
||||
@@ -248,7 +265,7 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
|
||||
// ulimit is smaller than aveage batch size
|
||||
// user must have very small ulimit
|
||||
// decrease batch size to half of ulimit
|
||||
warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimt with '--ulimt 5000'. ");
|
||||
warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. ");
|
||||
info!("Halving batch_size because ulimit is smaller than average batch size");
|
||||
batch_size = ulimit / 2
|
||||
} else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT {
|
||||
@@ -272,7 +289,7 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{adjust_ulimit_size, infer_batch_size, print_opening, Opts};
|
||||
use crate::{adjust_ulimit_size, infer_batch_size, print_opening, Opts, ScanOrder};
|
||||
use std::{net::IpAddr, str::FromStr};
|
||||
|
||||
#[test]
|
||||
@@ -284,6 +301,8 @@ mod tests {
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
};
|
||||
let batch_size = infer_batch_size(&opts, 120);
|
||||
|
||||
@@ -299,6 +318,8 @@ mod tests {
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
};
|
||||
let batch_size = infer_batch_size(&opts, 9_000);
|
||||
|
||||
@@ -315,6 +336,8 @@ mod tests {
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
};
|
||||
let batch_size = infer_batch_size(&opts, 5_000);
|
||||
|
||||
@@ -330,6 +353,8 @@ mod tests {
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
};
|
||||
let batch_size = adjust_ulimit_size(&opts);
|
||||
|
||||
@@ -350,6 +375,8 @@ mod tests {
|
||||
timeout: 1_000,
|
||||
ulimit: None,
|
||||
command: Vec::new(),
|
||||
accessible: true,
|
||||
scan_order: ScanOrder::Serial,
|
||||
};
|
||||
|
||||
infer_batch_size(&opts, 1_000_000);
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
mod range_iterator;
|
||||
use super::ScanOrder;
|
||||
use range_iterator::RangeIterator;
|
||||
|
||||
/// Represents options of port scanning.
|
||||
///
|
||||
/// Right now all these options involve ranges, but in the future
|
||||
/// it will also contain custom lists of ports.
|
||||
pub enum PortStrategy {
|
||||
Serial(SerialRange),
|
||||
Random(RandomRange),
|
||||
}
|
||||
|
||||
impl PortStrategy {
|
||||
pub fn pick(start: u16, end: u16, scan_type: ScanOrder) -> Self {
|
||||
match scan_type {
|
||||
ScanOrder::Serial => PortStrategy::Serial(SerialRange { start, end }),
|
||||
ScanOrder::Random => PortStrategy::Random(RandomRange { start, end }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn order(&self) -> Vec<u16> {
|
||||
match self {
|
||||
PortStrategy::Serial(range) => range.generate(),
|
||||
PortStrategy::Random(range) => range.generate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait associated with a port strategy. Each PortStrategy must be able
|
||||
/// to generate an order for future port scanning.
|
||||
trait RangeOrder {
|
||||
fn generate(&self) -> Vec<u16>;
|
||||
}
|
||||
|
||||
/// As the name implies SerialRange will always generate a vector in
|
||||
/// ascending order.
|
||||
pub struct SerialRange {
|
||||
start: u16,
|
||||
end: u16,
|
||||
}
|
||||
|
||||
impl RangeOrder for SerialRange {
|
||||
fn generate(&self) -> Vec<u16> {
|
||||
(self.start..self.end).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// As the name implies RandomRange will always generate a vector with
|
||||
/// a random order. This vector is built following the LCG algorithm.
|
||||
pub struct RandomRange {
|
||||
start: u16,
|
||||
end: u16,
|
||||
}
|
||||
|
||||
impl RangeOrder for RandomRange {
|
||||
// Right now using RangeIterator and generating a range + shuffling the
|
||||
// vector is pretty much the same. The advantages of it will come once
|
||||
// we have to generate different ranges for different IPs without storing
|
||||
// actual vectors.
|
||||
//
|
||||
// Another benefit of RangeIterator is that it always generate a range with
|
||||
// a certain distance between the items in the Array. The chances of having
|
||||
// port numbers close to each other are pretty slim due to the way the
|
||||
// algorithm works.
|
||||
fn generate(&self) -> Vec<u16> {
|
||||
RangeIterator::new(self.start.into(), self.end.into()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PortStrategy;
|
||||
use crate::ScanOrder;
|
||||
|
||||
#[test]
|
||||
fn serial_strategy() {
|
||||
let strategy = PortStrategy::pick(1, 100, ScanOrder::Serial);
|
||||
let result = strategy.order();
|
||||
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
|
||||
assert_eq!(expected_range, result);
|
||||
}
|
||||
#[test]
|
||||
fn random_strategy() {
|
||||
let strategy = PortStrategy::pick(1, 100, ScanOrder::Random);
|
||||
let mut result = strategy.order();
|
||||
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
|
||||
assert_ne!(expected_range, result);
|
||||
|
||||
result.sort();
|
||||
assert_eq!(expected_range, result);
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,10 @@ use gcd::Gcd;
|
||||
use rand::Rng;
|
||||
|
||||
pub struct RangeIterator {
|
||||
active: bool,
|
||||
normalized_end: u32,
|
||||
normalized_first_pick: Option<u32>,
|
||||
normalized_pick: Option<u32>,
|
||||
normalized_first_pick: u32,
|
||||
normalized_pick: u32,
|
||||
actual_start: u32,
|
||||
step: u32,
|
||||
}
|
||||
@@ -23,11 +24,17 @@ impl RangeIterator {
|
||||
let normalized_end = end - start;
|
||||
let step = pick_random_coprime(normalized_end);
|
||||
|
||||
// Randomly choose a number within the range to be the first
|
||||
// and assign it as a pick.
|
||||
let mut rng = rand::thread_rng();
|
||||
let normalized_first_pick = rng.gen_range(0, normalized_end);
|
||||
|
||||
Self {
|
||||
active: true,
|
||||
normalized_end,
|
||||
step,
|
||||
normalized_first_pick: None,
|
||||
normalized_pick: None,
|
||||
normalized_first_pick,
|
||||
normalized_pick: normalized_first_pick,
|
||||
actual_start: start,
|
||||
}
|
||||
}
|
||||
@@ -39,28 +46,21 @@ impl Iterator for RangeIterator {
|
||||
// The next step is always bound by the formula: N+1 = (N + STEP) % TOP_OF_THE_RANGE
|
||||
// It will only stop once we generate a number equal to the first generated number.
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// Randomly choose a number within the range to be the first
|
||||
// and assign it as a pick.
|
||||
if self.normalized_first_pick.is_none() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let normalized_first_pick = rng.gen_range(0, self.normalized_end);
|
||||
|
||||
self.normalized_first_pick = Some(normalized_first_pick);
|
||||
self.normalized_pick = Some(normalized_first_pick);
|
||||
return Some((self.actual_start + normalized_first_pick) as u16);
|
||||
if !self.active {
|
||||
return None;
|
||||
}
|
||||
|
||||
let current_pick = self.normalized_pick.unwrap();
|
||||
let current_pick = self.normalized_pick;
|
||||
let next_pick = (current_pick + self.step) % self.normalized_end;
|
||||
|
||||
// If the next pick is equal to the first pick this means that
|
||||
// we have iterated through the entire range.
|
||||
if next_pick == self.normalized_first_pick.unwrap() {
|
||||
return None;
|
||||
if next_pick == self.normalized_first_pick {
|
||||
self.active = false;
|
||||
}
|
||||
|
||||
self.normalized_pick = Some(next_pick);
|
||||
Some((self.actual_start + next_pick) as u16)
|
||||
self.normalized_pick = next_pick;
|
||||
Some((self.actual_start + current_pick) as u16)
|
||||
}
|
||||
}
|
||||
|
||||
+28
-38
@@ -1,5 +1,4 @@
|
||||
mod range_iterator;
|
||||
use range_iterator::RangeIterator;
|
||||
use super::PortStrategy;
|
||||
|
||||
use async_std::io;
|
||||
use async_std::net::TcpStream;
|
||||
@@ -21,28 +20,25 @@ use std::{
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub struct Scanner {
|
||||
hosts: Vec<IpAddr>,
|
||||
start: u16,
|
||||
end: u16,
|
||||
batch_size: u16,
|
||||
timeout: Duration,
|
||||
quiet: bool,
|
||||
port_strategy: PortStrategy,
|
||||
}
|
||||
|
||||
impl Scanner {
|
||||
pub fn new(
|
||||
hosts: &[IpAddr],
|
||||
start: u16,
|
||||
end: u16,
|
||||
batch_size: u16,
|
||||
timeout: Duration,
|
||||
quiet: bool,
|
||||
port_strategy: PortStrategy,
|
||||
) -> Self {
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
batch_size,
|
||||
timeout,
|
||||
quiet,
|
||||
port_strategy,
|
||||
hosts: hosts.iter().map(|host| host.to_owned()).collect(),
|
||||
}
|
||||
}
|
||||
@@ -51,27 +47,13 @@ impl Scanner {
|
||||
/// If you want to run RustScan normally, this is the entry point used
|
||||
/// Returns all open ports as Vec<u16>
|
||||
pub async fn run(&self) -> Vec<SocketAddr> {
|
||||
let range_iterator = RangeIterator::new(self.start.into(), self.end.into());
|
||||
let mut sockets: Vec<SocketAddr> = Vec::new();
|
||||
let ports: Vec<u16> = self.port_strategy.order();
|
||||
let batch_per_host: usize = self.batch_size as usize / self.hosts.len();
|
||||
let mut open_sockets: Vec<SocketAddr> = Vec::new();
|
||||
|
||||
for port in range_iterator {
|
||||
for host in &self.hosts {
|
||||
sockets.push(SocketAddr::new(*host, port));
|
||||
}
|
||||
|
||||
if sockets.len() >= self.batch_size.into() {
|
||||
let mut results = self.scan_range(&sockets).await;
|
||||
open_sockets.append(&mut results);
|
||||
sockets = Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
// This will happen if we have a number of sockets remaining
|
||||
// that is lower than the batch size.
|
||||
if !sockets.is_empty() {
|
||||
let mut results = self.scan_range(&sockets).await;
|
||||
open_sockets.append(&mut results);
|
||||
for batch in ports.chunks(batch_per_host) {
|
||||
let mut sockets = self.scan_ports(batch).await;
|
||||
open_sockets.append(&mut sockets);
|
||||
}
|
||||
|
||||
open_sockets
|
||||
@@ -79,10 +61,12 @@ impl Scanner {
|
||||
|
||||
/// Given a slice of sockets, scan them all.
|
||||
/// Returns a vector of open sockets.
|
||||
async fn scan_range(&self, sockets: &[SocketAddr]) -> Vec<SocketAddr> {
|
||||
async fn scan_ports(&self, ports: &[u16]) -> Vec<SocketAddr> {
|
||||
let mut ftrs = FuturesUnordered::new();
|
||||
for socket in sockets {
|
||||
ftrs.push(self.scan_socket(socket));
|
||||
for port in ports {
|
||||
for host in &self.hosts {
|
||||
ftrs.push(self.scan_socket(SocketAddr::new(*host, *port)));
|
||||
}
|
||||
}
|
||||
|
||||
let mut open_sockets: Vec<SocketAddr> = Vec::new();
|
||||
@@ -108,8 +92,8 @@ impl Scanner {
|
||||
/// self.scan_port(10:u16)
|
||||
///
|
||||
/// Note: `self` must contain `self.host`.
|
||||
async fn scan_socket(&self, socket: &SocketAddr) -> io::Result<SocketAddr> {
|
||||
match self.connect(*socket).await {
|
||||
async fn scan_socket(&self, socket: SocketAddr) -> io::Result<SocketAddr> {
|
||||
match self.connect(socket).await {
|
||||
Ok(x) => {
|
||||
// match stream_result.shutdown(Shutdown::Both)
|
||||
info!("Shutting down stream");
|
||||
@@ -120,7 +104,7 @@ impl Scanner {
|
||||
println!("Open {}", socket.to_string().purple());
|
||||
}
|
||||
|
||||
Ok(*socket)
|
||||
Ok(socket)
|
||||
}
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::Other => {
|
||||
@@ -155,6 +139,7 @@ impl Scanner {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ScanOrder;
|
||||
use async_std::task::block_on;
|
||||
use std::{net::IpAddr, time::Duration};
|
||||
|
||||
@@ -162,7 +147,8 @@ mod tests {
|
||||
fn scanner_runs() {
|
||||
// Makes sure the program still runs and doesn't panic
|
||||
let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
|
||||
let scanner = Scanner::new(&addrs, 1, 1_000, 10, Duration::from_millis(100), true);
|
||||
let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
|
||||
block_on(scanner.run());
|
||||
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
|
||||
assert_eq!(1, 1);
|
||||
@@ -171,7 +157,8 @@ mod tests {
|
||||
fn ipv6_scanner_runs() {
|
||||
// Makes sure the program still runs and doesn't panic
|
||||
let addrs = vec!["::1".parse::<IpAddr>().unwrap()];
|
||||
let scanner = Scanner::new(&addrs, 1, 1_000, 10, Duration::from_millis(100), false);
|
||||
let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
|
||||
block_on(scanner.run());
|
||||
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
|
||||
assert_eq!(1, 1);
|
||||
@@ -179,14 +166,16 @@ mod tests {
|
||||
#[test]
|
||||
fn quad_zero_scanner_runs() {
|
||||
let addrs = vec!["0.0.0.0".parse::<IpAddr>().unwrap()];
|
||||
let scanner = Scanner::new(&addrs, 1, 1_000, 10, Duration::from_millis(500), true);
|
||||
let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
#[test]
|
||||
fn google_dns_runs() {
|
||||
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
|
||||
let scanner = Scanner::new(&addrs, 400, 445, 10, Duration::from_millis(1_500), true);
|
||||
let strategy = PortStrategy::pick(400, 445, ScanOrder::Random);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
@@ -196,7 +185,8 @@ mod tests {
|
||||
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
|
||||
|
||||
// mac should have this automatically scaled down
|
||||
let scanner = Scanner::new(&addrs, 400, 600, 10_000, Duration::from_millis(1500), true);
|
||||
let strategy = PortStrategy::pick(400, 600, ScanOrder::Random);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user