mirror of
https://github.com/bee-san/RustScan.git
synced 2026-11-07 09:46:04 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f299dd1a1 | ||
|
|
7e1072d6a9 |
No files matched your search
+78
@@ -43,3 +43,81 @@ pkg/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/rust,archlinuxpackages
|
||||
*.deb
|
||||
|
||||
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
.idea/*
|
||||
.idea*
|
||||
.idea
|
||||
|
||||
Generated
+17
-1
@@ -322,6 +322,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@@ -514,6 +520,15 @@ dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.44"
|
||||
@@ -822,7 +837,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustscan"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"async-std",
|
||||
@@ -833,6 +848,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"futures",
|
||||
"gcd",
|
||||
"itertools",
|
||||
"log",
|
||||
"rand",
|
||||
"rlimit",
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustscan"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
authors = ["Bee <[email protected]>"]
|
||||
edition = "2018"
|
||||
description = "Faster Nmap Scanning with Rust"
|
||||
@@ -34,6 +34,7 @@ toml = "0.5.6"
|
||||
serde = "1.0.116"
|
||||
serde_derive = "1.0.116"
|
||||
cidr-utils = "0.5.0"
|
||||
itertools = "0.9.0"
|
||||
|
||||
[package.metadata.deb]
|
||||
depends = "$auto, nmap"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
| <p align="center"><a href="https://hub.docker.com/r/cmnatic/rustscan">🐋 Docker (Recommended) </a></p> | <p align="center"><a href="https://github.com/RustScan/RustScan/releases">👩💻 Kali / Debian </p> | <p align="center"><a href="https://aur.archlinux.org/packages/rustscan/">🏗️ Arch </a></p> | <p align="center"><a href="https://crates.io/crates/rustscan">🔧 Cargo (Universal) </a></p> |
|
||||
| ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/docker.png?raw=true" /></p> | <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/kali.png?raw=true" /></p> | <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/arch.png?raw=true" /></p> | <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/rust.png?raw=true" /></p> |
|
||||
| `docker pull rustscan/rustscan:v1.8.0` <p>[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` |
|
||||
| `docker pull rustscan/rustscan:v1.9.0` <p>[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` |
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -130,22 +130,22 @@ To install Docker, [follow their guide](https://docs.docker.com/engine/install/)
|
||||
|
||||
**Once Docker is installed, you can either build your own image using the `Dockerfile` (alpine) provided in the repo, or alternatively, use the published Docker image like below (most convenient)**
|
||||
|
||||
Please see our [DockerHub](https://hub.docker.com/repository/docker/rustscan/rustscan) for further our published versions. However, we recommend using our latest release [v1.8.0](https://github.com/RustScan/RustScan/releases/tag/1.8.0)
|
||||
Please see our [DockerHub](https://hub.docker.com/repository/docker/rustscan/rustscan) for further our published versions. However, we recommend using our latest release [v1.9.0](https://github.com/RustScan/RustScan/releases/tag/1.9.0)
|
||||
|
||||
```
|
||||
Stable and supported: rustscan/rustscan:v1.8.0
|
||||
Stable and supported: rustscan/rustscan:v1.9.0
|
||||
|
||||
Bleeding edge (run at your own risk!): rustscan/rustscan:latest
|
||||
```
|
||||
|
||||
We strongly recommend using the `v1.8.0` tag, as this is the latest major - stable - release of RustScan. This README uses the `v1.8.0` image by default, however, note that the `latest` image is considered experimental.
|
||||
We strongly recommend using the `v1.9.0` tag, as this is the latest major - stable - release of RustScan. This README uses the `v1.9.0` image by default, however, note that the `latest` image is considered experimental.
|
||||
|
||||
#### To get started:
|
||||
|
||||
Simply run this command against the IP you want to target:
|
||||
|
||||
```bash
|
||||
docker run -it --rm --name rustscan rustscan/rustscan:v1.8.0 <rustscan arguments here> <ip address to scan>
|
||||
docker run -it --rm --name rustscan rustscan/rustscan:v1.9.0 <rustscan arguments here> <ip address to scan>
|
||||
```
|
||||
|
||||
Note: this will scan the Docker's localhost, not your own.
|
||||
@@ -155,7 +155,7 @@ Once done, you will no longer need to re-download the image (except when RustSca
|
||||
You will have to run this command every time, so we suggest aliasing it to something memorable.
|
||||
|
||||
```bash
|
||||
alias rustscan='docker run -it --rm --name rustscan rustscan/rustscan:v1.8.0'
|
||||
alias rustscan='docker run -it --rm --name rustscan rustscan/rustscan:v1.9.0'
|
||||
```
|
||||
|
||||
Then we can scan:
|
||||
@@ -188,15 +188,7 @@ docker build -t <yourimagename> .
|
||||
|
||||
**Note for Mac users** Mac OS has a very, very small ulimit size. This will negatively impact RustScan by a significant amount. Please use the Docker container, or tell RustScan to up the ulimit size on every run.
|
||||
|
||||
Tap the brew:
|
||||
|
||||
```bash
|
||||
brew tap brandonskerritt/rustscan
|
||||
```
|
||||
|
||||
Install it:
|
||||
|
||||
```bash
|
||||
brew install rustscan
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ RustScan has 2 major labels for GitHub issues you should look at:
|
||||
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!
|
||||
These are issues that aren't really for newcomers, but we could still do with help!
|
||||
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22)
|
||||
|
||||
If you want to, solve the issue or comment on the issue for help.
|
||||
|
||||
+92
-125
@@ -1,5 +1,7 @@
|
||||
use serde_derive::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use structopt::{clap::arg_enum, StructOpt};
|
||||
|
||||
const LOWEST_PORT_NUMBER: u16 = 1;
|
||||
@@ -23,6 +25,7 @@ pub struct PortRange {
|
||||
pub end: u16,
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
fn parse_range(input: &str) -> Result<PortRange, String> {
|
||||
let range = input
|
||||
.split("-")
|
||||
@@ -70,9 +73,9 @@ pub struct Opts {
|
||||
#[structopt(short, long)]
|
||||
pub no_config: bool,
|
||||
|
||||
/// Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
|
||||
/// Greppable mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
|
||||
#[structopt(short, long)]
|
||||
pub quiet: bool,
|
||||
pub greppable: bool,
|
||||
|
||||
/// Accessible mode. Turns off features which negatively affect screen readers.
|
||||
#[structopt(long)]
|
||||
@@ -103,6 +106,10 @@ pub struct Opts {
|
||||
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
|
||||
pub scan_order: ScanOrder,
|
||||
|
||||
/// Input file containing IP addresses delimited by new lines
|
||||
#[structopt(short, long)]
|
||||
pub input: Option<PathBuf>,
|
||||
|
||||
/// 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'.
|
||||
@@ -110,8 +117,13 @@ pub struct Opts {
|
||||
/// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"")
|
||||
#[structopt(last = true)]
|
||||
pub command: Vec<String>,
|
||||
|
||||
/// Use the top 1000 ports.
|
||||
#[structopt(long)]
|
||||
pub top: bool,
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl Opts {
|
||||
pub fn read() -> Self {
|
||||
let mut opts = Opts::from_args();
|
||||
@@ -146,7 +158,7 @@ impl Opts {
|
||||
}
|
||||
}
|
||||
|
||||
merge_required!(addresses, quiet, accessible, batch_size, timeout, scan_order, command);
|
||||
merge_required!(addresses, greppable, accessible, batch_size, timeout, scan_order, command);
|
||||
}
|
||||
|
||||
fn merge_optional(&mut self, config: &Config) {
|
||||
@@ -160,28 +172,42 @@ impl Opts {
|
||||
}
|
||||
}
|
||||
|
||||
merge_optional!(ports, range, ulimit);
|
||||
// Only use top ports when the user asks for them
|
||||
if self.top {
|
||||
if config.ports.is_some() {
|
||||
let mut ports: Vec<u16> = Vec::with_capacity(config.ports.clone().unwrap().len());
|
||||
for entry in config.ports.clone().unwrap().keys() {
|
||||
ports.push(entry.parse().unwrap())
|
||||
}
|
||||
self.ports = Some(ports);
|
||||
}
|
||||
}
|
||||
|
||||
merge_optional!(range, ulimit);
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct used to deserialize the options specified within our config file.
|
||||
/// These will be further merged with our command line arguments in order to
|
||||
/// generate the final Opts struct.
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
addresses: Option<Vec<String>>,
|
||||
ports: Option<Vec<u16>>,
|
||||
ports: Option<HashMap<String, u16>>,
|
||||
range: Option<PortRange>,
|
||||
quiet: Option<bool>,
|
||||
greppable: Option<bool>,
|
||||
accessible: Option<bool>,
|
||||
batch_size: Option<u16>,
|
||||
timeout: Option<u32>,
|
||||
no_nmap: Option<bool>,
|
||||
ulimit: Option<rlimit::rlim>,
|
||||
scan_order: Option<ScanOrder>,
|
||||
input: Option<PathBuf>,
|
||||
command: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl Config {
|
||||
/// Reads the configuration file with TOML format and parses it into a
|
||||
/// Config struct.
|
||||
@@ -194,40 +220,21 @@ impl Config {
|
||||
/// scan_order: "Serial"
|
||||
///
|
||||
pub fn read() -> Self {
|
||||
let mut paths = Vec::new();
|
||||
paths.push(match dirs::home_dir() {
|
||||
Some(mut path) => {
|
||||
path.push(".rustscan.toml");
|
||||
path
|
||||
}
|
||||
let mut home_dir = match dirs::home_dir() {
|
||||
Some(dir) => dir,
|
||||
None => panic!("Could not infer config file path."),
|
||||
});
|
||||
};
|
||||
home_dir.push(".rustscan.toml");
|
||||
|
||||
paths.push(match dirs::config_dir() {
|
||||
Some(mut path) => {
|
||||
path.push("rustscan");
|
||||
path.push("config.toml");
|
||||
path
|
||||
let mut content = String::new();
|
||||
if home_dir.exists() {
|
||||
content = match fs::read_to_string(home_dir) {
|
||||
Ok(content) => content,
|
||||
Err(_) => String::new(),
|
||||
}
|
||||
None => panic!("Could not infer config file path."),
|
||||
});
|
||||
|
||||
let mut contents: Option<String> = None;
|
||||
for path in paths.iter().rev() {
|
||||
contents = match fs::read_to_string(path) {
|
||||
Ok(content) => {
|
||||
contents = Some(content);
|
||||
break;
|
||||
}
|
||||
Err(_) => None,
|
||||
};
|
||||
}
|
||||
|
||||
if contents.is_none() {
|
||||
contents = Some(String::new());
|
||||
}
|
||||
|
||||
let config: Config = match toml::from_str(&contents.unwrap()) {
|
||||
let config: Config = match toml::from_str(&content) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
println!("Found {} in configuration file.\nAborting scan.\n", e);
|
||||
@@ -242,42 +249,53 @@ impl Config {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Config, Opts, PortRange, ScanOrder};
|
||||
impl Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
addresses: Some(vec!["127.0.0.1".to_owned()]),
|
||||
ports: None,
|
||||
range: None,
|
||||
greppable: Some(true),
|
||||
batch_size: Some(25_000),
|
||||
timeout: Some(1_000),
|
||||
ulimit: None,
|
||||
no_nmap: Some(false),
|
||||
command: Some(vec!["-A".to_owned()]),
|
||||
accessible: Some(true),
|
||||
scan_order: Some(ScanOrder::Random),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Opts {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
addresses: vec![],
|
||||
ports: None,
|
||||
range: None,
|
||||
greppable: true,
|
||||
batch_size: 0,
|
||||
timeout: 0,
|
||||
ulimit: None,
|
||||
command: vec![],
|
||||
accessible: false,
|
||||
no_nmap: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: true,
|
||||
top: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opts_no_merge_when_config_is_ignored() {
|
||||
let mut opts = Opts {
|
||||
addresses: vec![],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: false,
|
||||
batch_size: 0,
|
||||
timeout: 0,
|
||||
ulimit: None,
|
||||
command: vec![],
|
||||
accessible: false,
|
||||
no_nmap: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: true,
|
||||
};
|
||||
|
||||
let config = Config {
|
||||
addresses: Some(vec!["127.0.0.1".to_owned()]),
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: Some(true),
|
||||
batch_size: Some(25_000),
|
||||
timeout: Some(1_000),
|
||||
ulimit: None,
|
||||
no_nmap: Some(false),
|
||||
command: Some(vec!["-A".to_owned()]),
|
||||
accessible: Some(true),
|
||||
scan_order: Some(ScanOrder::Random),
|
||||
};
|
||||
let mut opts = Opts::default();
|
||||
let config = Config::default();
|
||||
|
||||
opts.merge(&config);
|
||||
|
||||
assert_eq!(opts.addresses, vec![] as Vec<String>);
|
||||
assert_eq!(opts.quiet, false);
|
||||
assert_eq!(opts.greppable, true);
|
||||
assert_eq!(opts.accessible, false);
|
||||
assert_eq!(opts.timeout, 0);
|
||||
assert_eq!(opts.command, vec![] as Vec<String>);
|
||||
@@ -286,39 +304,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn opts_merge_required_arguments() {
|
||||
let mut opts = Opts {
|
||||
addresses: vec![],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: false,
|
||||
batch_size: 0,
|
||||
timeout: 0,
|
||||
ulimit: None,
|
||||
command: vec![],
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_nmap: false,
|
||||
no_config: false,
|
||||
};
|
||||
|
||||
let config = Config {
|
||||
addresses: Some(vec!["127.0.0.1".to_owned()]),
|
||||
ports: None,
|
||||
no_nmap: Some(false),
|
||||
range: None,
|
||||
quiet: Some(true),
|
||||
batch_size: Some(25_000),
|
||||
timeout: Some(1_000),
|
||||
ulimit: None,
|
||||
command: Some(vec!["-A".to_owned()]),
|
||||
accessible: Some(true),
|
||||
scan_order: Some(ScanOrder::Random),
|
||||
};
|
||||
let mut opts = Opts::default();
|
||||
let config = Config::default();
|
||||
|
||||
opts.merge_required(&config);
|
||||
|
||||
assert_eq!(opts.addresses, config.addresses.unwrap());
|
||||
assert_eq!(opts.quiet, config.quiet.unwrap());
|
||||
assert_eq!(opts.greppable, config.greppable.unwrap());
|
||||
assert_eq!(opts.timeout, config.timeout.unwrap());
|
||||
assert_eq!(opts.command, config.command.unwrap());
|
||||
assert_eq!(opts.accessible, config.accessible.unwrap());
|
||||
@@ -327,41 +319,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn opts_merge_optional_arguments() {
|
||||
let mut opts = Opts {
|
||||
addresses: vec![],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: false,
|
||||
batch_size: 0,
|
||||
timeout: 0,
|
||||
ulimit: None,
|
||||
command: vec![],
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_nmap: false,
|
||||
no_config: false,
|
||||
};
|
||||
|
||||
let config = Config {
|
||||
addresses: None,
|
||||
ports: Some(vec![80, 403]),
|
||||
range: Some(PortRange {
|
||||
start: 1,
|
||||
end: 1_000,
|
||||
}),
|
||||
quiet: None,
|
||||
batch_size: None,
|
||||
timeout: None,
|
||||
no_nmap: Some(false),
|
||||
ulimit: Some(1_000),
|
||||
command: None,
|
||||
accessible: None,
|
||||
scan_order: None,
|
||||
};
|
||||
let mut opts = Opts::default();
|
||||
let mut config = Config::default();
|
||||
config.range = Some(PortRange {
|
||||
start: 1,
|
||||
end: 1_000,
|
||||
});
|
||||
config.ulimit = Some(1_000);
|
||||
|
||||
opts.merge_optional(&config);
|
||||
|
||||
assert_eq!(opts.ports, config.ports);
|
||||
assert_eq!(opts.range, config.range);
|
||||
assert_eq!(opts.ulimit, config.ulimit);
|
||||
}
|
||||
|
||||
+102
-168
@@ -21,8 +21,12 @@ use futures::executor::block_on;
|
||||
use rlimit::Resource;
|
||||
use rlimit::{getrlimit, setrlimit};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use std::{net::IpAddr, time::Duration};
|
||||
|
||||
extern crate colorful;
|
||||
@@ -50,14 +54,18 @@ fn main() {
|
||||
|
||||
debug!("Main() `opts` arguments are {:?}", opts);
|
||||
|
||||
if !opts.quiet && !opts.accessible {
|
||||
print_opening();
|
||||
if !opts.greppable && !opts.accessible {
|
||||
print_opening(&opts);
|
||||
}
|
||||
|
||||
let ips: Vec<IpAddr> = parse_addresses(&opts);
|
||||
|
||||
if ips.is_empty() {
|
||||
warning!("No IPs could be resolved, aborting scan.", false);
|
||||
warning!(
|
||||
"No IPs could be resolved, aborting scan.",
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -68,8 +76,9 @@ fn main() {
|
||||
&ips,
|
||||
batch_size,
|
||||
Duration::from_millis(opts.timeout.into()),
|
||||
opts.quiet,
|
||||
opts.greppable,
|
||||
PortStrategy::pick(opts.range, opts.ports, opts.scan_order),
|
||||
opts.accessible,
|
||||
);
|
||||
debug!("Scanner finished building: {:?}", scanner);
|
||||
|
||||
@@ -101,33 +110,36 @@ fn main() {
|
||||
ip,
|
||||
opts.batch_size,
|
||||
"'rustscan -b <batch_size> <ip address>'");
|
||||
warning!(x, opts.quiet);
|
||||
warning!(x, opts.greppable, opts.accessible);
|
||||
}
|
||||
|
||||
let mut nmap_bench = NamedTimer::start("Nmap");
|
||||
for (ip, ports) in ports_per_ip.iter_mut() {
|
||||
let nmap_str_ports: Vec<String> = ports.into_iter().map(|port| port.to_string()).collect();
|
||||
|
||||
detail!("Starting Nmap", opts.quiet || opts.no_nmap);
|
||||
|
||||
// nmap port style is 80,443. Comma separated with no spaces.
|
||||
let ports_str = nmap_str_ports.join(",");
|
||||
|
||||
// if quiet mode is on nmap should not be spawned
|
||||
if opts.quiet || opts.no_nmap {
|
||||
if opts.greppable || opts.no_nmap {
|
||||
println!("{} -> [{}]", &ip, ports_str);
|
||||
continue;
|
||||
}
|
||||
detail!("Starting Nmap", opts.greppable, opts.accessible);
|
||||
|
||||
let addr = ip.to_string();
|
||||
let user_nmap_args =
|
||||
shell_words::split(&opts.command.join(" ")).expect("failed to parse nmap arguments");
|
||||
let nmap_args = build_nmap_arguments(&addr, &ports_str, &user_nmap_args, ip.is_ipv6());
|
||||
|
||||
output!(format!(
|
||||
"The Nmap command to be run is nmap {}\n",
|
||||
&nmap_args.join(" ")
|
||||
));
|
||||
output!(
|
||||
format!(
|
||||
"The Nmap command to be run is nmap {}\n",
|
||||
&nmap_args.join(" ")
|
||||
),
|
||||
opts.greppable.clone(),
|
||||
opts.accessible.clone()
|
||||
);
|
||||
|
||||
// Runs the nmap command and spawns it as a process.
|
||||
let mut child = Command::new("nmap")
|
||||
@@ -148,7 +160,7 @@ fn main() {
|
||||
}
|
||||
|
||||
/// Prints the opening title of RustScan
|
||||
fn print_opening() {
|
||||
fn print_opening(opts: &Opts) {
|
||||
debug!("Printing opening");
|
||||
let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-.
|
||||
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
|
||||
@@ -163,19 +175,17 @@ Faster Nmap scanning with Rust."#;
|
||||
println!("{}", info.gradient(Color::Yellow).bold());
|
||||
funny_opening!();
|
||||
|
||||
let config_path = match dirs::config_dir() {
|
||||
Some(mut path) => {
|
||||
path.push("rustscan");
|
||||
path.push("config.toml");
|
||||
path
|
||||
}
|
||||
None => panic!("Couldn't find config dir."),
|
||||
let mut home_dir = match dirs::home_dir() {
|
||||
Some(dir) => dir,
|
||||
None => panic!("Could not infer config file path."),
|
||||
};
|
||||
home_dir.push(".rustscan.toml");
|
||||
|
||||
detail!(format!(
|
||||
"{} {:?}",
|
||||
"The config file is expected to be at", config_path
|
||||
));
|
||||
detail!(
|
||||
format!("The config file is expected to be at {:?}", home_dir),
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
}
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
fn build_nmap_arguments<'a>(
|
||||
@@ -198,6 +208,24 @@ fn build_nmap_arguments<'a>(
|
||||
arguments
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
/// Parses an input file of IPs and uses those
|
||||
fn read_ips_from_file(ips: String) -> Result<Vec<std::net::IpAddr>, std::io::Error> {
|
||||
// if we cannot open it as a file, it is not a file so move on
|
||||
let file = File::open(ips)?;
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
let mut ips: Vec<std::net::IpAddr> = Vec::new();
|
||||
|
||||
for str_ip in reader.lines() {
|
||||
match IpAddr::from_str(&str_ip.unwrap()) {
|
||||
Ok(x) => ips.push(x),
|
||||
Err(y) => panic!("File does not contain valid IP address. Error at {}", y),
|
||||
}
|
||||
}
|
||||
Ok(ips)
|
||||
}
|
||||
|
||||
fn parse_addresses(opts: &Opts) -> Vec<IpAddr> {
|
||||
let mut ips: Vec<IpAddr> = Vec::new();
|
||||
|
||||
@@ -207,11 +235,18 @@ fn parse_addresses(opts: &Opts) -> Vec<IpAddr> {
|
||||
_ => match format!("{}:{}", &ip_or_host, 80).to_socket_addrs() {
|
||||
Ok(mut iter) => ips.push(iter.nth(0).unwrap().ip()),
|
||||
_ => {
|
||||
let failed_to_resolve = format!("Host {:?} could not be resolved.", ip_or_host);
|
||||
warning!(failed_to_resolve, opts.quiet);
|
||||
warning!(
|
||||
format!("Host {:?} could not be resolved.", ip_or_host),
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
match read_ips_from_file(ip_or_host.to_owned()) {
|
||||
Ok(x) => ips.extend(x),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
ips
|
||||
@@ -225,10 +260,17 @@ fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
|
||||
Ok(_) => {
|
||||
detail!(
|
||||
format!("Automatically increasing ulimit value to {}.", limit),
|
||||
opts.quiet
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
warning!(
|
||||
"ERROR. Failed to set ulimit value.",
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
}
|
||||
Err(_) => println!("{}", "ERROR. Failed to set ulimit value."),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +285,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 --ulimit. May cause harm to sensitive servers",
|
||||
opts.quiet
|
||||
opts.greppable, opts.accessible
|
||||
);
|
||||
|
||||
// When the OS supports high file limits like 8000, but the user
|
||||
@@ -253,7 +295,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 Ulimit with '--ulimit 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'. ", opts.greppable, opts.accessible);
|
||||
info!("Halving batch_size because ulimit is smaller than average batch size");
|
||||
batch_size = ulimit / 2
|
||||
} else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT {
|
||||
@@ -266,10 +308,8 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
|
||||
// When the ulimit is higher than the batch size let the user know that the
|
||||
// batch size can be increased unless they specified the ulimit themselves.
|
||||
else if ulimit + 2 > batch_size && (opts.ulimit.is_none()) {
|
||||
detail!(format!(
|
||||
"File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.",
|
||||
ulimit - 100
|
||||
), opts.quiet);
|
||||
detail!(format!("File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", ulimit - 100),
|
||||
opts.greppable, opts.accessible);
|
||||
}
|
||||
|
||||
batch_size as u16
|
||||
@@ -277,48 +317,22 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts, ScanOrder,
|
||||
};
|
||||
use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[test]
|
||||
fn batch_size_lowered() {
|
||||
let opts = Opts {
|
||||
addresses: vec!["127.0.0.1".to_owned()],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: true,
|
||||
batch_size: 50_000,
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: false,
|
||||
no_nmap: false,
|
||||
};
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 50_000;
|
||||
let batch_size = infer_batch_size(&opts, 120);
|
||||
|
||||
assert!(batch_size < 50_000);
|
||||
assert!(batch_size < opts.batch_size);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_size_lowered_average_size() {
|
||||
let opts = Opts {
|
||||
addresses: vec!["127.0.0.1".to_owned()],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: true,
|
||||
batch_size: 50_000,
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: false,
|
||||
no_nmap: false,
|
||||
};
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 50_000;
|
||||
let batch_size = infer_batch_size(&opts, 9_000);
|
||||
|
||||
assert!(batch_size == 3_000);
|
||||
@@ -327,20 +341,8 @@ mod tests {
|
||||
fn batch_size_equals_ulimit_lowered() {
|
||||
// because ulimit and batch size are same size, batch size is lowered
|
||||
// to ULIMIT - 100
|
||||
let opts = Opts {
|
||||
addresses: vec!["127.0.0.1".to_owned()],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: true,
|
||||
batch_size: 50_000,
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: false,
|
||||
no_nmap: false,
|
||||
};
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 50_000;
|
||||
let batch_size = infer_batch_size(&opts, 5_000);
|
||||
|
||||
assert!(batch_size == 4_900);
|
||||
@@ -348,68 +350,36 @@ mod tests {
|
||||
#[test]
|
||||
fn batch_size_adjusted_2000() {
|
||||
// ulimit == batch_size
|
||||
let opts = Opts {
|
||||
addresses: vec!["127.0.0.1".to_owned()],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: true,
|
||||
batch_size: 50_000,
|
||||
timeout: 1_000,
|
||||
ulimit: Some(2_000),
|
||||
command: Vec::new(),
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: false,
|
||||
no_nmap: false,
|
||||
};
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 50_000;
|
||||
opts.ulimit = Some(2_000);
|
||||
let batch_size = adjust_ulimit_size(&opts);
|
||||
|
||||
assert!(batch_size == 2_000);
|
||||
}
|
||||
#[test]
|
||||
fn test_print_opening_no_panic() {
|
||||
let mut opts = Opts::default();
|
||||
opts.ulimit = Some(2_000);
|
||||
// print opening should not panic
|
||||
print_opening();
|
||||
print_opening(&opts);
|
||||
assert!(1 == 1);
|
||||
}
|
||||
#[test]
|
||||
fn test_high_ulimit_no_quiet_mode() {
|
||||
let opts = Opts {
|
||||
addresses: vec!["127.0.0.1".to_owned()],
|
||||
ports: None,
|
||||
range: None,
|
||||
quiet: false,
|
||||
batch_size: 10,
|
||||
timeout: 1_000,
|
||||
ulimit: None,
|
||||
command: Vec::new(),
|
||||
accessible: true,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: false,
|
||||
no_nmap: false,
|
||||
};
|
||||
fn test_high_ulimit_no_greppable_mode() {
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 10;
|
||||
opts.greppable = false;
|
||||
|
||||
infer_batch_size(&opts, 1_000_000);
|
||||
let batch_size = infer_batch_size(&opts, 1_000_000);
|
||||
|
||||
assert!(1 == 1);
|
||||
assert!(batch_size == opts.batch_size);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_correct_addresses() {
|
||||
let opts = Opts {
|
||||
addresses: vec!["127.0.0.1".to_owned(), "192.168.0.0/30".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 mut opts = Opts::default();
|
||||
opts.addresses = vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()];
|
||||
let ips = parse_addresses(&opts);
|
||||
|
||||
assert_eq!(
|
||||
@@ -426,20 +396,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_correct_host_addresses() {
|
||||
let opts = Opts {
|
||||
addresses: vec!["google.com".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 mut opts = Opts::default();
|
||||
opts.addresses = vec!["google.com".to_owned()];
|
||||
let ips = parse_addresses(&opts);
|
||||
|
||||
assert_eq!(ips.len(), 1);
|
||||
@@ -447,20 +405,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_correct_and_incorrect_addresses() {
|
||||
let opts = Opts {
|
||||
addresses: vec!["127.0.0.1".to_owned(), "im_wrong".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 mut opts = Opts::default();
|
||||
opts.addresses = vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()];
|
||||
let ips = parse_addresses(&opts);
|
||||
|
||||
assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]);
|
||||
@@ -468,20 +414,8 @@ mod tests {
|
||||
|
||||
#[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 mut opts = Opts::default();
|
||||
opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()];
|
||||
let ips = parse_addresses(&opts);
|
||||
|
||||
assert_eq!(ips.is_empty(), true);
|
||||
|
||||
+17
-10
@@ -26,8 +26,9 @@ pub struct Scanner {
|
||||
ips: Vec<IpAddr>,
|
||||
batch_size: u16,
|
||||
timeout: Duration,
|
||||
quiet: bool,
|
||||
greppable: bool,
|
||||
port_strategy: PortStrategy,
|
||||
accessible: bool,
|
||||
}
|
||||
|
||||
impl Scanner {
|
||||
@@ -35,15 +36,17 @@ impl Scanner {
|
||||
ips: &[IpAddr],
|
||||
batch_size: u16,
|
||||
timeout: Duration,
|
||||
quiet: bool,
|
||||
greppable: bool,
|
||||
port_strategy: PortStrategy,
|
||||
accessible: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
batch_size,
|
||||
timeout,
|
||||
quiet,
|
||||
greppable,
|
||||
port_strategy,
|
||||
ips: ips.iter().map(|ip| ip.to_owned()).collect(),
|
||||
accessible,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +110,12 @@ impl Scanner {
|
||||
Err(e) => debug!("Shutdown stream error {}", &e),
|
||||
_ => {}
|
||||
}
|
||||
if !self.quiet {
|
||||
println!("Open {}", socket.to_string().purple());
|
||||
if !self.greppable {
|
||||
if self.accessible {
|
||||
println!("Open {}", socket.to_string());
|
||||
} else {
|
||||
println!("Open {}", socket.to_string().purple());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(socket)
|
||||
@@ -167,7 +174,7 @@ 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);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), 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);
|
||||
@@ -181,7 +188,7 @@ 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);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), 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);
|
||||
@@ -194,7 +201,7 @@ 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);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
@@ -206,7 +213,7 @@ 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);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
@@ -221,7 +228,7 @@ 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);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
use itertools::{iproduct, Product};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
pub struct SocketIterator<'s> {
|
||||
ips: &'s [IpAddr],
|
||||
ports: &'s [u16],
|
||||
ip_idx: usize,
|
||||
ip_len: usize,
|
||||
port_idx: usize,
|
||||
port_len: usize,
|
||||
// product_it is a cartesian product iterator over
|
||||
// the slices of ports and IP addresses.
|
||||
//
|
||||
// The IP/port order is intentionally reversed here since we want
|
||||
// the itertools::iproduct! macro below to generate the pairs with
|
||||
// all the IPs for one port before moving on to the next one
|
||||
// ("hold the port, go through all the IPs, then advance the port...").
|
||||
// See also the comments in the iterator implementation for an example.
|
||||
product_it:
|
||||
Product<Box<std::slice::Iter<'s, u16>>, Box<std::slice::Iter<'s, std::net::IpAddr>>>,
|
||||
}
|
||||
|
||||
/// An iterator that receives a slice of IPs and ports and returns a Socket
|
||||
@@ -16,13 +21,10 @@ pub struct SocketIterator<'s> {
|
||||
/// generating a vector containing all these combinations.
|
||||
impl<'s> SocketIterator<'s> {
|
||||
pub fn new(ips: &'s [IpAddr], ports: &'s [u16]) -> Self {
|
||||
let ports_it = Box::new(ports.into_iter());
|
||||
let ips_it = Box::new(ips.into_iter());
|
||||
Self {
|
||||
ip_idx: 0,
|
||||
ip_len: ips.len(),
|
||||
port_idx: 0,
|
||||
port_len: ports.len(),
|
||||
ips,
|
||||
ports,
|
||||
product_it: iproduct!(ports_it, ips_it),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,20 +43,10 @@ impl<'s> Iterator for SocketIterator<'s> {
|
||||
/// it.next(); // 192.168.0.1:443
|
||||
/// it.next(); // None
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.port_idx == self.port_len {
|
||||
return None;
|
||||
match self.product_it.next() {
|
||||
None => None,
|
||||
Some((port, ip)) => Some(SocketAddr::new(*ip, *port)),
|
||||
}
|
||||
|
||||
self.ip_idx = self.ip_idx % self.ip_len;
|
||||
|
||||
let socket = SocketAddr::new(self.ips[self.ip_idx], self.ports[self.port_idx]);
|
||||
self.ip_idx += 1;
|
||||
|
||||
if self.ip_idx == self.ip_len {
|
||||
self.port_idx += 1;
|
||||
}
|
||||
|
||||
Some(socket)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-13
@@ -7,11 +7,16 @@ macro_rules! warning {
|
||||
use ansi_term::Colour::Red;
|
||||
println!("{} {}", Red.bold().paint("[!]"), $name);
|
||||
};
|
||||
// quiet mode
|
||||
($name:expr, $quiet:expr) => {
|
||||
($name:expr, $greppable:expr, $accessible:expr) => {
|
||||
use ansi_term::Colour::Red;
|
||||
if !$quiet {
|
||||
println!("{} {}", Red.bold().paint("[!]"), $name);
|
||||
// if not greppable then print, otherwise no else statement so do not print.
|
||||
if !$greppable {
|
||||
if $accessible {
|
||||
// Don't print the ascii art
|
||||
println!("{}", $name);
|
||||
} else {
|
||||
println!("{} {}", Red.bold().paint("[!]"), $name);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -22,26 +27,38 @@ macro_rules! detail {
|
||||
use ansi_term::Colour::Blue;
|
||||
println!("{} {}", Blue.bold().paint("[~]"), $name);
|
||||
};
|
||||
($name:expr, $quiet:expr) => {
|
||||
($name:expr, $greppable:expr, $accessible:expr) => {
|
||||
use ansi_term::Colour::Blue;
|
||||
if !$quiet {
|
||||
println!("{} {}", Blue.bold().paint("[~]"), $name);
|
||||
// if not greppable then print, otherwise no else statement so do not print.
|
||||
if !$greppable {
|
||||
if $accessible {
|
||||
// Don't print the ascii art
|
||||
println!("{}", $name);
|
||||
} else {
|
||||
println!("{} {}", Blue.bold().paint("[~]"), $name);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! output {
|
||||
($name:expr, $quiet:expr) => {
|
||||
use ansi_term::Colour::RGB;
|
||||
if !$quiet {
|
||||
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
|
||||
}
|
||||
};
|
||||
($name:expr) => {
|
||||
use ansi_term::Colour::RGB;
|
||||
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
|
||||
};
|
||||
($name:expr, $greppable:expr, $accessible:expr) => {
|
||||
use ansi_term::Colour::RGB;
|
||||
// if not greppable then print, otherwise no else statement so do not print.
|
||||
if !$greppable {
|
||||
if $accessible {
|
||||
// Don't print the ascii art
|
||||
println!("{}", $name);
|
||||
} else {
|
||||
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
||||
Reference in new issue
Block a user