diff --git a/Cargo.lock b/Cargo.lock index d673c82..2463c39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,6 +208,19 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cidr-utils" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f7b6372f3c783f6d44c0da283d3694af4104967bf2371cb3bdc9d09e1cfe00" +dependencies = [ + "debug-helper", + "num-bigint", + "num-traits", + "once_cell", + "regex", +] + [[package]] name = "clap" version = "2.33.2" @@ -266,6 +279,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "debug-helper" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a5bb894f24f42c247f19b25928a88e31867c0f84552c05df41a9dd527435e" + [[package]] name = "dirs" version = "3.0.1" @@ -521,6 +540,36 @@ dependencies = [ "fastrand", ] +[[package]] +name = "num-bigint" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -763,6 +812,7 @@ version = "1.8.0" dependencies = [ "ansi_term 0.12.1", "async-std", + "cidr-utils", "colored", "colorful", "dirs", diff --git a/Cargo.toml b/Cargo.toml index a4ee885..783b498 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ ansi_term = "0.12.1" toml = "0.5.6" serde = "1.0.115" serde_derive = "1.0.115" +cidr-utils = "0.5.0" [package.metadata.deb] depends = "$auto, nmap" diff --git a/README.md b/README.md index 5474abd..79dbdb3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@

AUR version -GitHub commit activity +GitHub commit activity GitHub All Releases Crates.io Discord @@ -21,10 +21,10 @@


-|

🐋 Docker (Recommended)

|

👩‍💻 Kali / Debian

|

🏗️ Arch

|

🔧 Cargo (Universal)

| -| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -|

|

|

|

| -| `docker pull rustscan/rustscan:alpine`

[Usage](https://github.com/RustScan/RustScan#docker-whale) | [Read the install guide](https://github.com/brandonskerritt/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) | `yay -S rustscan` | `cargo install rustscan` | +|

🐋 Docker (Recommended)

|

👩‍💻 Kali / Debian

|

🏗️ Arch

|

🔧 Cargo (Universal)

| +| ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | +|

|

|

|

| +| `docker pull rustscan/rustscan:alpine`

[Usage](https://github.com/RustScan/RustScan#docker-whale) | [Read the install guide](https://github.com/Rustscan/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) | `yay -S rustscan` | `cargo install rustscan` |


@@ -111,7 +111,7 @@ Note: sometimes Rust doesn't add Cargo to the path. Please see [this issue](http Download the .deb file from the releases page: -[https://github.com/brandonskerritt/RustScan/releases](https://github.com/brandonskerritt/RustScan/releases) +[https://github.com/RustScan/RustScan/releases](https://github.com/RustScan/RustScan/releases) Run the command `dpkg -i` on the file. @@ -229,14 +229,15 @@ server may not be able to handle this many socket connections at once. - Discord https://github.com/RustScan/RustScan USAGE: - rustscan [FLAGS] [OPTIONS] [ips-or-hosts]... [-- ...] + rustscan [FLAGS] [OPTIONS] [addresses]... [-- ...] FLAGS: - --accessible Accessible mode. Turns off features which negatively affect screen readers - -h, --help Prints help information - -i, --ignore-config Whether to ignore the configuration file or not - -q, --quiet Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file - -V, --version Prints version information + --accessible Accessible mode. Turns off features which negatively affect screen readers + -h, --help Prints help information + -n, --no-config Whether to ignore the configuration file or not + --no-nmap Turns off Nmap + -q, --quiet Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file + -V, --version Prints version information OPTIONS: -b, --batch-size The batch size for port scanning, it increases or slows the speed of scanning. @@ -251,11 +252,11 @@ OPTIONS: -u, --ulimit Automatically ups the ULIMIT with the value you provided ARGS: - ... A list of comma separated IP addresses or hosts to be scanned - ... 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)'\"") + ... A list of comma separated CIDRs, IPs, or hosts to be scanned + ... 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. @@ -269,7 +270,7 @@ Your operating system may not support this, but it is worth it to play around an This binary accepts a configuration file that is read from the home directory of the user. It follows the TOML format and accepts the following fields: -- `ips_or_hosts` +- `addresses` - `ports` - `range` - `scan_order` @@ -283,7 +284,7 @@ and accepts the following fields: ### Format example ```toml -ips_or_hosts = ["127.0.0.1", "192.168.0.1", "www.google.com"] +addresses = ["127.0.0.1", "192.168.0.0/30", "www.google.com"] command = ["-A"] ports = [80, 443, 8080] range = { start = 1, end = 10 } @@ -364,7 +365,7 @@ 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. **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. +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/RustScan/RustScan/issues/25) for more. ## 🔌 Nmap Custom Flags @@ -432,7 +433,9 @@ Please read the [contributing.md file](contributing.md) ## Contributors ✨ + [![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors-) + Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -469,6 +472,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d + This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/src/input.rs b/src/input.rs index 0bab9b0..267cee6 100644 --- a/src/input.rs +++ b/src/input.rs @@ -54,9 +54,9 @@ fn parse_range(input: &str) -> Result { /// - Discord https://discord.gg/GFrQsGy /// - GitHub https://github.com/RustScan/RustScan pub struct Opts { - /// A list of comma separated IP addresses or hosts to be scanned. + /// A list of comma separated CIDRs, IPs, or hosts to be scanned. #[structopt(use_delimiter = true)] - pub ips_or_hosts: Vec, + pub addresses: Vec, /// A list of comma separed ports to be scanned. Example: 80,443,8080. #[structopt(short, long, use_delimiter = true)] @@ -146,15 +146,7 @@ impl Opts { } } - merge_required!( - ips_or_hosts, - quiet, - accessible, - batch_size, - timeout, - scan_order, - command - ); + merge_required!(addresses, quiet, accessible, batch_size, timeout, scan_order, command); } fn merge_optional(&mut self, config: &Config) { @@ -177,7 +169,7 @@ impl Opts { /// generate the final Opts struct. #[derive(Debug, Deserialize)] pub struct Config { - ips_or_hosts: Option>, + addresses: Option>, ports: Option>, range: Option, quiet: Option, @@ -196,7 +188,7 @@ impl Config { /// /// # Format /// - /// ips_or_hosts = ["127.0.0.1", "127.0.0.1"] + /// addresses = ["127.0.0.1", "127.0.0.1"] /// ports = [80, 443, 8080] /// quiet = true /// scan_order: "Serial" @@ -254,7 +246,7 @@ mod tests { #[test] fn opts_no_merge_when_config_is_ignored() { let mut opts = Opts { - ips_or_hosts: vec![], + addresses: vec![], ports: None, range: None, quiet: false, @@ -269,7 +261,7 @@ mod tests { }; let config = Config { - ips_or_hosts: Some(vec!["127.0.0.1".to_owned()]), + addresses: Some(vec!["127.0.0.1".to_owned()]), ports: None, range: None, quiet: Some(true), @@ -284,7 +276,7 @@ mod tests { opts.merge(&config); - assert_eq!(opts.ips_or_hosts, vec![] as Vec); + assert_eq!(opts.addresses, vec![] as Vec); assert_eq!(opts.quiet, false); assert_eq!(opts.accessible, false); assert_eq!(opts.timeout, 0); @@ -295,7 +287,7 @@ mod tests { #[test] fn opts_merge_required_arguments() { let mut opts = Opts { - ips_or_hosts: vec![], + addresses: vec![], ports: None, range: None, quiet: false, @@ -310,7 +302,7 @@ mod tests { }; let config = Config { - ips_or_hosts: Some(vec!["127.0.0.1".to_owned()]), + addresses: Some(vec!["127.0.0.1".to_owned()]), ports: None, no_nmap: Some(false), range: None, @@ -325,7 +317,7 @@ mod tests { opts.merge_required(&config); - assert_eq!(opts.ips_or_hosts, config.ips_or_hosts.unwrap()); + assert_eq!(opts.addresses, config.addresses.unwrap()); assert_eq!(opts.quiet, config.quiet.unwrap()); assert_eq!(opts.timeout, config.timeout.unwrap()); assert_eq!(opts.command, config.command.unwrap()); @@ -336,7 +328,7 @@ mod tests { #[test] fn opts_merge_optional_arguments() { let mut opts = Opts { - ips_or_hosts: vec![], + addresses: vec![], ports: None, range: None, quiet: false, @@ -351,7 +343,7 @@ mod tests { }; let config = Config { - ips_or_hosts: None, + addresses: None, ports: Some(vec![80, 403]), range: Some(PortRange { start: 1, diff --git a/src/main.rs b/src/main.rs index f11642b..6fcd46b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,16 +11,15 @@ use scanner::Scanner; mod port_strategy; use port_strategy::PortStrategy; +use cidr_utils::cidr::IpCidr; use colorful::Color; use colorful::Colorful; use futures::executor::block_on; use rlimit::Resource; use rlimit::{getrlimit, setrlimit}; - use std::collections::HashMap; use std::net::ToSocketAddrs; use std::process::Command; -use std::str::FromStr; use std::{net::IpAddr, time::Duration}; extern crate colorful; @@ -51,7 +50,7 @@ fn main() { print_opening(); } - let ips: Vec = parse_ips(&opts); + let ips: Vec = parse_addresses(&opts); if ips.is_empty() { warning!("No IPs could be resolved, aborting scan.", false); @@ -183,12 +182,12 @@ fn build_nmap_arguments<'a>( arguments } -fn parse_ips(opts: &Opts) -> Vec { +fn parse_addresses(opts: &Opts) -> Vec { let mut ips: Vec = Vec::new(); - for ip_or_host in &opts.ips_or_hosts { - match IpAddr::from_str(ip_or_host) { - Ok(ip) => ips.push(ip), + for ip_or_host in &opts.addresses { + match IpCidr::from_str(ip_or_host) { + Ok(cidr) => cidr.iter().for_each(|ip| ips.push(ip)), _ => match format!("{}:{}", &ip_or_host, 80).to_socket_addrs() { Ok(mut iter) => ips.push(iter.nth(0).unwrap().ip()), _ => { @@ -262,12 +261,15 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 { #[cfg(test)] mod tests { - use crate::{adjust_ulimit_size, infer_batch_size, parse_ips, print_opening, Opts, ScanOrder}; + use crate::{ + adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts, ScanOrder, + }; + use std::net::Ipv4Addr; #[test] fn batch_size_lowered() { let opts = Opts { - ips_or_hosts: vec!["127.0.0.1".to_owned()], + addresses: vec!["127.0.0.1".to_owned()], ports: None, range: None, quiet: true, @@ -288,7 +290,7 @@ mod tests { #[test] fn batch_size_lowered_average_size() { let opts = Opts { - ips_or_hosts: vec!["127.0.0.1".to_owned()], + addresses: vec!["127.0.0.1".to_owned()], ports: None, range: None, quiet: true, @@ -310,7 +312,7 @@ mod tests { // because ulimit and batch size are same size, batch size is lowered // to ULIMIT - 100 let opts = Opts { - ips_or_hosts: vec!["127.0.0.1".to_owned()], + addresses: vec!["127.0.0.1".to_owned()], ports: None, range: None, quiet: true, @@ -331,7 +333,7 @@ mod tests { fn batch_size_adjusted_2000() { // ulimit == batch_size let opts = Opts { - ips_or_hosts: vec!["127.0.0.1".to_owned()], + addresses: vec!["127.0.0.1".to_owned()], ports: None, range: None, quiet: true, @@ -357,7 +359,7 @@ mod tests { #[test] fn test_high_ulimit_no_quiet_mode() { let opts = Opts { - ips_or_hosts: vec!["127.0.0.1".to_owned()], + addresses: vec!["127.0.0.1".to_owned()], ports: None, range: None, quiet: false, @@ -377,9 +379,9 @@ mod tests { } #[test] - fn parse_correct_ips_or_hosts() { + fn parse_correct_addresses() { let opts = Opts { - ips_or_hosts: vec!["127.0.0.1".to_owned(), "google.com".to_owned()], + addresses: vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()], ports: None, range: None, quiet: true, @@ -392,15 +394,24 @@ mod tests { no_config: false, no_nmap: false, }; - let ips = parse_ips(&opts); + let ips = parse_addresses(&opts); - assert_eq!(2, ips.len()); + assert_eq!( + ips, + [ + Ipv4Addr::new(127, 0, 0, 1), + Ipv4Addr::new(192, 168, 0, 0), + Ipv4Addr::new(192, 168, 0, 1), + Ipv4Addr::new(192, 168, 0, 2), + Ipv4Addr::new(192, 168, 0, 3) + ] + ); } #[test] - fn parse_correct_and_incorrect_ips_or_hosts() { + fn parse_correct_host_addresses() { let opts = Opts { - ips_or_hosts: vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()], + addresses: vec!["google.com".to_owned()], ports: None, range: None, quiet: true, @@ -413,15 +424,15 @@ mod tests { no_config: false, no_nmap: false, }; - let ips = parse_ips(&opts); + let ips = parse_addresses(&opts); - assert_eq!(1, ips.len()); + assert_eq!(ips.len(), 1); } #[test] - fn parse_incorrect_ips_or_hosts() { + fn parse_correct_and_incorrect_addresses() { let opts = Opts { - ips_or_hosts: vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()], + addresses: vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()], ports: None, range: None, quiet: true, @@ -434,8 +445,29 @@ mod tests { no_config: false, no_nmap: false, }; - let ips = parse_ips(&opts); + let ips = parse_addresses(&opts); - assert_eq!(0, ips.len()); + assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]); + } + + #[test] + fn parse_incorrect_addresses() { + let opts = Opts { + addresses: vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()], + ports: None, + range: None, + quiet: true, + batch_size: 10, + timeout: 1_000, + ulimit: Some(2_000), + command: Vec::new(), + accessible: false, + scan_order: ScanOrder::Serial, + no_config: false, + no_nmap: false, + }; + let ips = parse_addresses(&opts); + + assert_eq!(ips.is_empty(), true); } }