Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ OPTIONS:
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]
--tries <tries> The number of tries before a port is assumed to be closed. If set to 0, rustscan
will correct it to 1 [default: 1]
-u, --ulimit <ulimit> Automatically ups the ULIMIT with the value you provided

ARGS:
Expand All @@ -260,7 +262,7 @@ Your operating system may not support this, but it is worth it to play around an

## Configuration file

This binary accepts a configuration file that is read from the home directory of the user. It follows the TOML format
This binary accepts a configuration file, named `.rustscan.toml`, that is read from the home directory of the user. It follows the TOML format
and accepts the following fields:

- `addresses`
Expand All @@ -272,6 +274,7 @@ and accepts the following fields:
- `greppable`
- `batch-size`
- `timeout`
- `tries`
- `ulimit`

### Format example
Expand All @@ -286,6 +289,7 @@ accessible = true
scan_order = "Serial"
batch_size = 1000
timeout = 1000
tries = 3
ulimit = 1000
```

Expand Down
12 changes: 11 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ pub struct Opts {
#[structopt(short, long, default_value = "1500")]
pub timeout: u32,

/// The number of tries before a port is assumed to be closed.
/// If set to 0, rustscan will correct it to 1.
#[structopt(long, default_value = "1")]
pub tries: u8,

/// Automatically ups the ULIMIT with the value you provided.
#[structopt(short, long)]
pub ulimit: Option<rlimit::rlim>,
Expand Down Expand Up @@ -154,7 +159,9 @@ impl Opts {
}
}

merge_required!(addresses, greppable, accessible, batch_size, timeout, scan_order, command);
merge_required!(
addresses, greppable, accessible, batch_size, timeout, tries, scan_order, command
);
}

fn merge_optional(&mut self, config: &Config) {
Expand Down Expand Up @@ -196,6 +203,7 @@ pub struct Config {
accessible: Option<bool>,
batch_size: Option<u16>,
timeout: Option<u32>,
tries: Option<u8>,
no_nmap: Option<bool>,
ulimit: Option<rlimit::rlim>,
scan_order: Option<ScanOrder>,
Expand Down Expand Up @@ -253,6 +261,7 @@ mod tests {
greppable: Some(true),
batch_size: Some(25_000),
timeout: Some(1_000),
tries: Some(1),
ulimit: None,
no_nmap: Some(false),
command: Some(vec!["-A".to_owned()]),
Expand All @@ -271,6 +280,7 @@ mod tests {
greppable: true,
batch_size: 0,
timeout: 0,
tries: 0,
ulimit: None,
command: vec![],
accessible: false,
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ fn main() {
&ips,
batch_size,
Duration::from_millis(opts.timeout.into()),
opts.tries,
opts.greppable,
PortStrategy::pick(opts.range, opts.ports, opts.scan_order),
opts.accessible,
Expand Down
132 changes: 94 additions & 38 deletions src/scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use futures::stream::FuturesUnordered;
use std::{
io::ErrorKind,
net::{IpAddr, Shutdown, SocketAddr},
num::NonZeroU8,
time::Duration,
};

Expand All @@ -26,6 +27,7 @@ pub struct Scanner {
ips: Vec<IpAddr>,
batch_size: u16,
timeout: Duration,
tries: NonZeroU8,
greppable: bool,
port_strategy: PortStrategy,
accessible: bool,
Expand All @@ -36,13 +38,15 @@ impl Scanner {
ips: &[IpAddr],
batch_size: u16,
timeout: Duration,
tries: u8,
greppable: bool,
port_strategy: PortStrategy,
accessible: bool,
) -> Self {
Self {
batch_size,
timeout,
tries: NonZeroU8::new(std::cmp::max(tries, 1)).unwrap(),
greppable,
port_strategy,
ips: ips.iter().map(|ip| ip.to_owned()).collect(),
Expand Down Expand Up @@ -87,54 +91,66 @@ impl Scanner {
open_sockets
}

/// Given a port, scan it.
/// Given a socket, scan it self.tries times.
/// Turns the address into a SocketAddr
/// Deals with the <result> type
/// If it experiences error ErrorKind::Other then too many files are open and it Panics!
/// ese any other error, it returns the error in Result as a string
/// If no errors occur, it returns the port number in Result to signify the port is open.
/// Else any other error, it returns the error in Result as a string
/// If no errors occur, it returns the port number in Result to signify the port is open.
/// This function mainly deals with the logic of Results handling.
/// # Example
///
/// self.scan_port(10:u16)
/// self.scan_socket(socket)
///
/// Note: `self` must contain `self.ip`.
async fn scan_socket(&self, socket: SocketAddr) -> io::Result<SocketAddr> {
match self.connect(socket).await {
Ok(x) => {
debug!(
"Connection was successful, shutting down stream {}",
&socket
);
match x.shutdown(Shutdown::Both) {
Err(e) => debug!("Shutdown stream error {}", &e),
_ => {}
}
if !self.greppable {
if self.accessible {
println!("Open {}", socket.to_string());
} else {
println!("Open {}", socket.to_string().purple());
let tries = self.tries.get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following my last comment , now we can use tries normally, so this would become let tries = self.tries;


debug!("self.tries: {}", tries);

for nr_try in 1..=tries {
debug!("Try number: {}", nr_try);

match self.connect(socket).await {
Ok(x) => {
debug!(
"Connection was successful, shutting down stream {}",
&socket
);
if let Err(e) = x.shutdown(Shutdown::Both) {
debug!("Shutdown stream error {}", &e);
}
if !self.greppable {
if self.accessible {
println!("Open {}", socket.to_string());
} else {
println!("Open {}", socket.to_string().purple());
}
}

debug!("Return Ok after {} tries", nr_try);
return Ok(socket);
}
Err(e) => {
let error_string = e.to_string();

Ok(socket)
}
Err(e) => match e.kind() {
ErrorKind::Other => {
if e.to_string().contains("No route to host")
|| e.to_string().contains("Network is unreachable")
{
debug!("Socket connect error: {} {}", &e.to_string(), &socket);
Err(io::Error::new(io::ErrorKind::Other, e.to_string()))
} else {
debug!("Socket connect error: {} {}", &e.to_string(), &socket);
panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
if e.kind() == ErrorKind::Other {
debug!("Socket connect error: {} {}", &error_string, &socket);

if !error_string.contains("No route to host")
&& !error_string.contains("Network is unreachable")
{
panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
}
}

if nr_try == tries {
return Err(io::Error::new(io::ErrorKind::Other, error_string));
}
}
_ => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
},
};
}
unreachable!();
}

/// Performs the connection to the socket with timeout
Expand Down Expand Up @@ -174,7 +190,15 @@ mod tests {
end: 1_000,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
Expand All @@ -188,7 +212,15 @@ mod tests {
end: 1_000,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
Expand All @@ -201,7 +233,15 @@ mod tests {
end: 1_000,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
assert_eq!(1, 1);
}
Expand All @@ -213,7 +253,15 @@ mod tests {
end: 445,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
assert_eq!(1, 1);
}
Expand All @@ -228,7 +276,15 @@ mod tests {
end: 600,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
assert_eq!(1, 1);
}
Expand Down