mirror of
https://github.com/bee-san/RustScan.git
synced 2026-11-07 09:46:04 -10:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b64aa19d61 | ||
|
|
81ad00dc29 | ||
|
|
64cac6e0fa | ||
|
|
739e9716fc | ||
|
|
6bf70f0f9a | ||
|
|
57dfb15852 | ||
|
|
282065752c | ||
|
|
982ae0d926 | ||
|
|
53f8a15e16 | ||
|
|
fc214a78b4 | ||
|
|
0ae289c25f | ||
|
|
9326fc4a0f | ||
|
|
c05ebaf857 | ||
|
|
6f8df7e77e | ||
|
|
488853f40e | ||
|
|
57c1e33650 | ||
|
|
699b578ade | ||
|
|
ebe5a326a8 | ||
|
|
abb7a3872c | ||
|
|
f74cb456b1 | ||
|
|
492d2fb3b3 | ||
|
|
af53591ccb | ||
|
|
6405b43f5a | ||
|
|
9a71253697 |
No files matched your search
@@ -193,6 +193,15 @@
|
||||
"bug",
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dmitris",
|
||||
"name": "Dmitry Savintsev",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/31205?v=4",
|
||||
"profile": "https://github.com/dmitris",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
+77
@@ -43,3 +43,80 @@ 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
+711
-14
File diff suppressed because it is too large.
Load diff
+9
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustscan"
|
||||
version = "1.8.0"
|
||||
version = "1.10.0"
|
||||
authors = ["Bee <[email protected]>"]
|
||||
edition = "2018"
|
||||
description = "Faster Nmap Scanning with Rust"
|
||||
@@ -18,7 +18,7 @@ exclude = [
|
||||
|
||||
[dependencies]
|
||||
colored = "2.0.0"
|
||||
structopt = "0.3.17"
|
||||
structopt = "0.3.18"
|
||||
async-std = "1.6.4"
|
||||
futures = "0.3"
|
||||
rlimit = "0.4.0"
|
||||
@@ -34,6 +34,9 @@ toml = "0.5.6"
|
||||
serde = "1.0.116"
|
||||
serde_derive = "1.0.116"
|
||||
cidr-utils = "0.5.0"
|
||||
itertools = "0.9.0"
|
||||
trust-dns-resolver = { version = "0.19.5"}
|
||||
criterion = "0.3"
|
||||
|
||||
[package.metadata.deb]
|
||||
depends = "$auto, nmap"
|
||||
@@ -42,3 +45,7 @@ section = "rust"
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
|
||||
[[bench]]
|
||||
name = "main"
|
||||
harness = false
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
FROM rust:alpine as builder
|
||||
LABEL maintainer="CMNatic <https://github.com/CMNatic">
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
# Encourage some layer caching here rather then copying entire directory that includes docs to builder container ~CMN
|
||||
WORKDIR /usr/src/rustscan
|
||||
COPY . .
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY src src
|
||||
RUN cargo install --path .
|
||||
|
||||
FROM alpine:3.12
|
||||
@@ -13,4 +16,4 @@ RUN addgroup -S rustscan && \
|
||||
apk add --no-cache nmap nmap-scripts wget
|
||||
COPY --from=builder /usr/local/cargo/bin/rustscan /usr/local/bin/rustscan
|
||||
USER rustscan
|
||||
ENTRYPOINT [ "/usr/local/bin/rustscan" ]
|
||||
ENTRYPOINT [ "/usr/local/bin/rustscan" ]
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -435,7 +427,7 @@ Please read the [contributing.md file](contributing.md)
|
||||
## Contributors ✨
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
@@ -468,6 +460,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://liberapay.com/Artem4/"><img src="https://avatars0.githubusercontent.com/u/5614476?v=4" width="100px;" alt=""/><br /><sub><b>Artem Polishchuk</b></sub></a><br /><a href="#platform-tim77" title="Packaging/porting to new platform">📦</a></td>
|
||||
<td align="center"><a href="https://github.com/buermarc"><img src="https://avatars2.githubusercontent.com/u/44375277?v=4" width="100px;" alt=""/><br /><sub><b>buermarc</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=buermarc" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/bergabman"><img src="https://avatars1.githubusercontent.com/u/44554109?v=4" width="100px;" alt=""/><br /><sub><b>bergabman</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=bergabman" title="Code">💻</a> <a href="https://github.com/RustScan/RustScan/issues?q=author%3Abergabman" title="Bug reports">🐛</a> <a href="#design-bergabman" title="Design">🎨</a></td>
|
||||
<td align="center"><a href="https://github.com/dmitris"><img src="https://avatars0.githubusercontent.com/u/31205?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Savintsev</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=dmitris" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use rustscan::*;
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
+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.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
127.0.0.1
|
||||
google.com
|
||||
example.com
|
||||
66666666666666.666666666666666666.66666666666666666.6666666666
|
||||
....
|
||||
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.radiatorFixtures
|
||||
+86
-125
@@ -1,4 +1,5 @@
|
||||
use serde_derive::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use structopt::{clap::arg_enum, StructOpt};
|
||||
|
||||
@@ -23,6 +24,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 +72,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)]
|
||||
@@ -110,8 +112,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 +153,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,19 +167,31 @@ 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>,
|
||||
@@ -182,6 +201,7 @@ pub struct Config {
|
||||
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 +214,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 +243,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 +298,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 +313,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);
|
||||
}
|
||||
|
||||
+162
-173
@@ -21,9 +21,14 @@ 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::{net::IpAddr, time::Duration};
|
||||
use trust_dns_resolver::config::*;
|
||||
use trust_dns_resolver::Resolver;
|
||||
|
||||
extern crate colorful;
|
||||
extern crate dirs;
|
||||
@@ -50,14 +55,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 +77,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 +111,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 +161,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 +176,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,25 +209,96 @@ fn build_nmap_arguments<'a>(
|
||||
arguments
|
||||
}
|
||||
|
||||
/// Goes through all possible IP inputs (files or via argparsing)
|
||||
/// Parses the string(s) into IPs
|
||||
fn parse_addresses(opts: &Opts) -> Vec<IpAddr> {
|
||||
let mut ips: Vec<IpAddr> = Vec::new();
|
||||
let resolver = &Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();
|
||||
|
||||
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()),
|
||||
match read_ips_from_file(ip_or_host.to_owned(), &resolver) {
|
||||
Ok(x) => ips.extend(x),
|
||||
_ => match parse_to_ip(ip_or_host.to_owned(), &resolver) {
|
||||
Ok(x) => ips.extend(x),
|
||||
_ => {
|
||||
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
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
ips
|
||||
}
|
||||
|
||||
/// Uses DNS to get the IPS assiocated with host
|
||||
fn resolve_ips_from_host(
|
||||
source: &String,
|
||||
resolver: &Resolver,
|
||||
) -> Result<Vec<IpAddr>, std::io::Error> {
|
||||
let mut ips: Vec<std::net::IpAddr> = Vec::new();
|
||||
|
||||
match resolver.lookup_ip(&source) {
|
||||
Ok(x) => {
|
||||
for ip in x.iter() {
|
||||
ips.push(ip);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
return Ok(ips);
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
/// Parses an input file of IPs and uses those
|
||||
fn read_ips_from_file(
|
||||
ips: String,
|
||||
resolver: &Resolver,
|
||||
) -> 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 str_ip {
|
||||
Ok(x) => match parse_to_ip(x, resolver) {
|
||||
Ok(result) => ips.extend(result),
|
||||
Err(e) => {
|
||||
debug!("{} is not a valid IP or host", e);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
debug!("Line in file is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ips)
|
||||
}
|
||||
|
||||
/// Given a string, parse it as an host, IP address, or CIDR.
|
||||
/// This allows us to pass files as hosts or cidr or IPs easily
|
||||
/// Call this everytime you have a possible IP_or_host
|
||||
fn parse_to_ip(address: String, resolver: &Resolver) -> Result<Vec<IpAddr>, std::io::Error> {
|
||||
let mut ips: Vec<IpAddr> = Vec::new();
|
||||
|
||||
match IpCidr::from_str(&address) {
|
||||
Ok(cidr) => cidr.iter().for_each(|ip| ips.push(ip)),
|
||||
_ => match format!("{}:{}", &address, 80).to_socket_addrs() {
|
||||
Ok(mut iter) => ips.push(iter.nth(0).unwrap().ip()),
|
||||
_ => match resolve_ips_from_host(&address, resolver) {
|
||||
Ok(hosts) => ips.extend(hosts),
|
||||
_ => (),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Ok(ips)
|
||||
}
|
||||
|
||||
fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
|
||||
if opts.ulimit.is_some() {
|
||||
let limit: rlimit::rlim = opts.ulimit.unwrap();
|
||||
@@ -225,10 +307,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 +332,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 +342,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 +355,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 +364,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 +388,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 +397,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 +443,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 +452,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,22 +461,18 @@ 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);
|
||||
}
|
||||
#[test]
|
||||
fn parse_hosts_file_and_incorrect_hosts() {
|
||||
// Host file contains IP, Hosts, incorrect IPs, incorrect hosts
|
||||
let mut opts = Opts::default();
|
||||
opts.addresses = vec!["fixtures/hosts.txt".to_owned()];
|
||||
let ips = parse_addresses(&opts);
|
||||
assert_eq!(ips.len(), 3);
|
||||
}
|
||||
}
|
||||
Submodule src/rustscan_scripting_engine deleted from 13c3fabcac.
+62
-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,52 @@ 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);
|
||||
}
|
||||
#[test]
|
||||
fn scanner_runs_serial() {
|
||||
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
|
||||
|
||||
let range = PortRange {
|
||||
start: 400,
|
||||
end: 600,
|
||||
};
|
||||
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Serial);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
#[test]
|
||||
fn scanner_doesnt_run_no_range() {
|
||||
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
|
||||
|
||||
let range = PortRange { start: 0, end: 0 };
|
||||
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Serial);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
#[test]
|
||||
fn scanner_doesnt_run_high_range() {
|
||||
let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
|
||||
|
||||
let range = PortRange {
|
||||
start: 1,
|
||||
end: 65535,
|
||||
};
|
||||
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Serial);
|
||||
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
#[test]
|
||||
fn scanner_doesnt_run_backwards_range() {
|
||||
let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
|
||||
let range = PortRange { start: 10, end: 1 };
|
||||
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Serial);
|
||||
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