Compare commits

..
Author SHA1 Message Date
Brandon 6a281bf34e 1.5 release 2020-08-04 15:21:30 +01:00
27 changed files with 651 additions and 3761 deletions

No files matched your search

-126
View File
@@ -76,132 +76,6 @@
"ideas",
"design"
]
},
{
"login": "Atul9",
"name": "Atul Bhosale",
"avatar_url": "https://avatars1.githubusercontent.com/u/3390330?v=4",
"profile": "https://github.com/Atul9",
"contributions": [
"code"
]
},
{
"login": "TGotwig",
"name": "Thomas Gotwig",
"avatar_url": "https://avatars0.githubusercontent.com/u/30773779?v=4",
"profile": "https://tgotwig.me",
"contributions": [
"platform"
]
},
{
"login": "remigourdon",
"name": "Rémi Gourdon",
"avatar_url": "https://avatars3.githubusercontent.com/u/2874133?v=4",
"profile": "https://github.com/remigourdon",
"contributions": [
"doc",
"code"
]
},
{
"login": "cmnatic",
"name": "Ben (CMNatic)",
"avatar_url": "https://avatars3.githubusercontent.com/u/4163116?v=4",
"profile": "https://cmnatic.co.uk",
"contributions": [
"code",
"doc",
"design"
]
},
{
"login": "Ferryistaken",
"name": "Alessandro Ferrari",
"avatar_url": "https://avatars3.githubusercontent.com/u/47927670?v=4",
"profile": "https://github.com/Ferryistaken",
"contributions": [
"content"
]
},
{
"login": "Phenomite",
"name": "Phenomite",
"avatar_url": "https://avatars2.githubusercontent.com/u/8285537?v=4",
"profile": "https://github.com/Phenomite",
"contributions": [
"content"
]
},
{
"login": "SuperSandro2000",
"name": "Sandro",
"avatar_url": "https://avatars2.githubusercontent.com/u/7258858?v=4",
"profile": "https://supersandro.de/",
"contributions": [
"content",
"bug",
"code"
]
},
{
"login": "caass",
"name": "Cass",
"avatar_url": "https://avatars2.githubusercontent.com/u/25358963?v=4",
"profile": "https://swag.lgbt",
"contributions": [
"platform",
"code",
"bug"
]
},
{
"login": "niklasmohrin",
"name": "Niklas Mohrin",
"avatar_url": "https://avatars0.githubusercontent.com/u/47574893?v=4",
"profile": "https://github.com/niklasmohrin",
"contributions": [
"doc"
]
},
{
"login": "tim77",
"name": "Artem Polishchuk",
"avatar_url": "https://avatars0.githubusercontent.com/u/5614476?v=4",
"profile": "https://liberapay.com/Artem4/",
"contributions": [
"platform"
]
},
{
"login": "buermarc",
"name": "buermarc",
"avatar_url": "https://avatars2.githubusercontent.com/u/44375277?v=4",
"profile": "https://github.com/buermarc",
"contributions": [
"code"
]
},
{
"login": "bergabman",
"name": "bergabman",
"avatar_url": "https://avatars1.githubusercontent.com/u/44554109?v=4",
"profile": "https://github.com/bergabman",
"contributions": [
"code",
"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,
-2
View File
@@ -1,2 +0,0 @@
.git
Dockerfile
+1 -1
View File
@@ -1,4 +1,4 @@
github: bee-san
github: brandonskerritt
patreon: brandonskerritt
ko_fi: hacker
custom: ["https://monzo.me/bee", "https://paypal.me/brandonskerritt"]
-4
View File
@@ -1,4 +0,0 @@
template: |
## What’s Changed
$CHANGES
-77
View File
@@ -43,80 +43,3 @@ 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*
-3
View File
@@ -1,3 +0,0 @@
[submodule "src/rustscan_scripting_engine"]
path = src/rustscan_scripting_engine
url = https://github.com/RustScan/rustscan_scripting_engine
+42 -23
View File
@@ -1,37 +1,56 @@
language: rust
os:
- linux
- osx
- linux
- osx
# - windows
arch:
- amd64
- arm64
- amd64
- arm64
services:
- docker
- docker
before_install:
- rustup component add rustfmt
# cargo test
rust:
- stable
- beta
- stable
- beta
jobs:
fast_finish: true
# Cargo fmt
script:
- cargo build
- cargo test
- cargo fmt -- --check
# automatic deployment to cargo on release
# deploy:
# provider: releases
# api_key: "GITHUB OAUTH TOKEN"
# file: "FILE TO UPLOAD"
# skip_cleanup: true
# on:
# tags: true
# after_success:
# - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
# - chmod +x send.sh
# - ./send.sh success $WEBHOOK_URL
# only deploy on master branch
# on:
# branch: master
# provider: cargo
# token: $crates
after_failure:
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
- chmod +x send.sh
- "./send.sh failure $WEBHOOK_URL"
after_success: |
if [ -n "${GITHUB_TOKEN}" ]; then
[ "${TRAVIS_BRANCH} " = master ] &&
[ "${TRAVIS_PULL_REQUEST}" = false ] &&
cargo doc &&
echo "<meta http-equiv=refresh content=0;url=rustscan/index.html>" > target/doc/index.html &&
sudo pip install ghp-import &&
ghp-import -n target/doc &&
git push -fq https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
fi
env:
global:
secure: Gf24LG7Hr3Q0drWyPrvISTL35Z57DxdS/wIOo7v+ZdauzY3Qjvv3Lh4TY//C4sdN0g1CLj4yjg7LQOqF4XRdh2Qah7JZPv/i2B5uz9nwjTsoRzmmMWML9VtfT1u53nSFqsNeXbvpmoNG9rfMXcoiyd+Lv+EgniHphDEBlXvS9n9KMcpOi1F2x7bsH0EK37wQYKMTpCCd5TPUPdZou7BJNiZS3ikRLpTRFF7uIN4gYtRH9kU5uBSphWgnJ6eBm2ItzZcUPOgrjAoF4RkmZAzGG4idDh1Qkf3No3nZwT3U5NSk8h7owtJ4oADlRgYqYKCB4YcC9cAKCJgDsyMlKKMkEyagvw1seFnBLCY3ljsW4EklpVEZ0rVqZOx4pTkIaxIxytLTC9pHmdDSnhdhFknOcVAQQWjg+nAZfD07QUeAqUdr0i/vxQoZitvbNY0znzEarycE1waKvwlkiB4gt385U1CBfYldydnOnRKkdtLNrCLClu13HU0djmSXMhojQJhTuNxTHtZAf5fKcWIWwkVbNZ5MNKYjQh4rKufqW3o1iHphB8dfAGsDEPv0RQMtVbu6H4+clCouLkFlxHOIrrOJ5SXQ8cIATPhnJR7y4cSDdRaN0DRLRjeLAIKlbOGBtNuFC+BftocaTY5qN42aKAMqQcT30ycfiLuRVJNiQJ5pkto=
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
- chmod +x send.sh
- ./send.sh failure $WEBHOOK_URL
+16 -16
View File
@@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at our discord via a private message to any of the owners. All
reported by contacting the project team at brandon@skerritt.blog. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
@@ -67,10 +67,10 @@ members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the Contributor Covenant homepage, version 1.4,
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
homepage: https://www.contributor-covenant.org
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
Generated
+59 -787
View File
File diff suppressed because it is too large. Load diff
+7 -23
View File
@@ -1,6 +1,6 @@
[package]
name = "rustscan"
version = "1.9.0"
version = "1.5.0"
authors = ["Bee <[email protected]>"]
edition = "2018"
description = "Faster Nmap Scanning with Rust"
@@ -10,37 +10,21 @@ license = "MIT"
keywords = ["port", "scanning", "nmap"]
categories = ["command-line-utilities"]
readme="README.md"
exclude = [
".github/*",
"pictures/*",
"rustscan-debbuilder/*",
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
colored = "2.0.0"
structopt = "0.3.18"
async-std = "1.6.4"
structopt = "0.3.15"
async-std = "1.6.2"
futures = "0.3"
rlimit = "0.4.0"
rlimit = "0.3.0"
shell-words = "1.0.0"
log = "0.4.0"
env_logger = "0.7.1"
dirs = "3.0.1"
gcd = "2.0.1"
rand = "0.7.3"
colorful = "0.2.1"
ansi_term = "0.12.1"
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", features = ["dns-over-rustls"] }
[package.metadata.deb]
depends = "$auto, nmap"
authors = ["Bee <[email protected]>"]
section = "rust"
[profile.release]
lto = true
panic = 'abort'
-19
View File
@@ -1,19 +0,0 @@
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 Cargo.toml Cargo.lock ./
COPY src src
RUN cargo install --path .
FROM alpine:3.12
LABEL author="Hydragyrum <https://github.com/Hydragyrum>"
RUN addgroup -S rustscan && \
adduser -S -G rustscan rustscan && \
ulimit -n 100000 && \
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" ]
-21
View File
@@ -1,21 +0,0 @@
install:
cargo install cross
build:
make build-linux
make build-mac
make shasum
build-linux:
@echo 'Building for Linux... 🐧'
cross build --release --target=x86_64-unknown-linux-musl
mkdir -p target/release-archives && tar -czf target/release-archives/rustscan-linux.tar.gz target/x86_64-unknown-linux-musl/release/rustscan
build-mac:
@echo 'Building for MacOS... 🍏'
cross build --release --target=x86_64-apple-darwin
mkdir -p target/release-archives && tar -czf target/release-archives/rustscan-mac.tar.gz target/x86_64-apple-darwin/release/rustscan
shasum:
shasum -a 256 target/release-archives/rustscan-*.tar.gz
+117 -238
View File
@@ -1,19 +1,19 @@
<p align="center">
➡️
➡️
<a href="https://discord.gg/GFrQsGy">Discord</a> |
<a href="https://github.com/RustScan/RustScan#-full-installation-guide">Installation Guide</a> |
<a href="https://github.com/RustScan/RustScan#-usage">Usage Guide</a>
⬅️
<br>
<img src="pictures/rustscan.png" height=400px width=400px>
<img src="pictures/rustscan.png">
</p>
<p align="center">
<u><b> Turns a 17 minutes Nmap scan into 19 seconds. </b></u><br> Find all open ports <b>fast</b> with RustScan, automatically pipe them into Nmap.
<u><b> Turns a 17 minutes Nmap scan into 19 seconds. </b></u><br> Find all open ports <b>fast</b> with RustScan, automatically pipe them into Nmap.
</p>
<p align="center">
<img alt="AUR version" src="https://img.shields.io/aur/version/rustscan">
<img alt="AUR version" src="https://img.shields.io/aur/version/rustscan-bin">
<img src="https://img.shields.io/badge/Built%20with-Rust-Purple">
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/w/Rustscan/Rustscan">
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/w/brandonskerritt/rustscan">
<img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/rustscan/rustscan/total?label=GitHub%20Downloads">
<img alt="Crates.io" src="https://img.shields.io/crates/d/rustscan?label=Cargo%20Downloads">
<img alt="Discord" src="https://img.shields.io/discord/736614461313515576">
@@ -21,16 +21,16 @@
</p>
<hr>
| <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.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` |
| <p align="center"><a href="https://crates.io/crates/rustscan">🔧 Cargo (Universal) </a></p> | <p align="center"><a href="https://crates.io/crates/rust_scan"> Arch </a></p> | <p align="center"><a href="https://crates.io/crates/rust_scan"> HomeBrew </a></p> | <p align="center"><a href="https://crates.io/crates/rust_scan"> Kali / Debian </p> |
| ---- | ---- | ---- | --- |
| <p align="center"><img src="pictures/rust.png" /></p> | <p align="center"><img src="pictures/arch.png" /></p> | <p align="center"><img src="pictures/apple.png" /></p> | <p align="center"><img src="pictures/kali.png" /></p> |
| `cargo install rustscan` | `yay -S rustscan` | `brew tap brandonskerritt/rustscan && brew install rustscan` | [Read the install guide](https://github.com/brandonskerritt/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) |
<hr>
# 🤔 What is this?
1. Find ports quickly using Rustscan (**3 seconds at its fastest**).
1. Find ports quickly using Rustscan (**8 seconds at its fastest**).
2. Automatically runs `nmap` on those ports.
3. ???
4. Profit!
@@ -43,8 +43,8 @@
</tr>
<tr>
<th>Gif</th>
<td><img src="https://github.com/RustScan/RustScan/blob/master/pictures/with_rustscan.gif?raw=true" alt="The guy she tells you not to worry about"></td>
<td><img src="https://github.com/RustScan/RustScan/blob/master/pictures/without_rustscan.gif?raw=true" alt="You"></td>
<td><img src="pictures/with_rustscan.gif" alt="The guy she tells you not to worry about"></td>
<td><img src="pictures/without_rustscan.gif" alt="You"></td>
</tr>
<tr>
<th>Time</th>
@@ -53,37 +53,21 @@
</tr>
</table>
<sub><b>Note</b> This is an older gif. RustScan's current top speed is 3 seconds for all 65k ports. This gif is 26 seconds.</sub><br>
<sub><b>Note</b> This is an older gif. RustScan's current top speed is 8 seconds for all 65k ports. This gif is 26 seconds.</sub><br>
RustScans **only** job is to reduce the friction between finding open ports and inputting them into nmap.
# ✨ Features
- Scans all 65k ports in **8 seconds** (on 10k batch size).
- Saves you time by automatically piping it into Nmap. No more manual copying and pasting!
- Does one thing and does it well. **Only** purpose is to improve Nmap, not replace it!
- Let's you choose what Nmap commands to run, or uses the default.
- IPv6 Support
## ‼️ Important Links
| Installation Guide | Documentation | Discord |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------- |
| 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://rustscan.github.io/RustScan/) | 🦜 [Discord](https://discord.gg/GFrQsGy) |
## 🙋 Table of Contents
- 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide)
- 🐋 [Docker Usage](https://github.com/RustScan/RustScan#docker-whale)
- 🦜 [Discord](https://discord.gg/GFrQsGy)
- 🤸 [Usage](https://github.com/RustScan/RustScan#-usage)
- 🎪 [Community](https://github.com/RustScan/RustScan#-community)
* Scans all 65k ports in **8 seconds** (on 10k batch size).
* Saves you time by automatically piping it into Nmap. No more manual copying and pasting!
* Does one thing and does it well. **Only** purpose is to improve Nmap, not replace it!
* Let's you choose what Nmap commands to run, or uses the default.
* IPv6 Support
# 🔭 Why RustScan?
Why spend time running fast scans and manually copying the ports, or waiting for a 20 minute scan to finish when you can just do all 65k ports in less than a minute?
![gif](https://github.com/RustScan/RustScan/blob/master/pictures/8seconds.gif?raw=true)
![gif](pictures/8seconds.gif)
**RustScan running in 8 seconds and finding all open ports out of 65k.**
@@ -91,19 +75,31 @@ Why spend time running fast scans and manually copying the ports, or waiting for
| **Name** | RustScan | Nmap | Masscan |
| ------------------------------------------------------------------------------------------ | -------- | ---- | ------- |
| Fast | ✅ | ❌ | ✅ |
| Actually useful | ❌ | ✅ | ❌ |
| Realises it's not useful, and pipes the only useful data into the only useful port scanner | ✅ | ❌ | ❌ |
| Fast | ✅ | ❌ | ✅ |
| Actually useful | ❌ | ✅ | ❌ |
| Realises it's not useful, and pipes the only useful data into the only useful port scanner | ✅ | ❌ | ❌ |
## ‼️ Important Links
| Installation Guide | Documentation | Discord |
| ------------------ | ------------- | ------- |
| 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://github.com/RustScan/RustScan/issues/89) | 🦜 [Discord](https://discord.gg/GFrQsGy)
## 🙋 Table of Contents
* 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide)
* 🦜 [Discord](https://discord.gg/GFrQsGy)
* 🤸 [Usage](https://github.com/RustScan/RustScan#-usage)
* 🎪 [Contributing](https://github.com/RustScan/RustScan#-contributing)
# 📖 Full Installation Guide
**You need Nmap**. If you have Kali Linux or Parrot OS installed, you already have Nmap. If not, [follow the nmap install guide](https://nmap.org/download.html).
The easiest way to install RustScan is to use one of the packages provided for your system, such as HomeBrew or Yay for Arch Linux.
The most universal way is to use `cargo`, Rust's built in package manager (think Pip but for Rust). [Follow this guide to installing Rust & Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html).
If you face any issues at all, please leave a GitHub issue. I have only tested this on Linux, so there may be issues for Mac OS or Windows.
If you face any issues at all, please leave a GitHub issue. I have only tested this on Linux, so there may be issues for Mac OS or Windows.
Note: sometimes Rust doesn't add Cargo to the path. Please see [this issue](https://github.com/rust-lang/rustup/issues/2436) for how to fix that.
@@ -111,82 +107,32 @@ 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/RustScan/RustScan/releases](https://github.com/RustScan/RustScan/releases)
[https://github.com/brandonskerritt/RustScan/releases](https://github.com/brandonskerritt/RustScan/releases)
Run the command `dpkg -i` on the file.
Run the commpand `dpkg -i` on the file.
Note: sometimes you can double click the file to achieve the same result.
## Docker :whale:
### 🥧 .deb file not working?
The .deb file only works on AMD64 CPUs. If yours is different (such as a Raspberry Pi) or the .deb file doesn't work, it is easy to build the .deb file yourself.
**Note**: It is easier to install Rust and install via Cargo, then it is to build the .deb file. But this is just in case!
Docker is the recommended way of installing RustScan. This is because:
- It has a high open file descriptor limit, which is one of the [main problems](https://github.com/RustScan/RustScan/issues/40) with RustScan. Now you don't have to fiddle around trying to understand your OS.
- It works on all systems, regardless of OS. Even Windows, which we don't officially support.
- The Docker image uses the latest build from Cargo, our main source-of-truth package. This means that you will always be using the latest version.
- No need to install Rust, Cargo, or Nmap.
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.9.0](https://github.com/RustScan/RustScan/releases/tag/1.9.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.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.9.0 <rustscan arguments here> <ip address to scan>
```
Note: this will scan the Docker's localhost, not your own.
Once done, you will no longer need to re-download the image (except when RustScan updates) and can use RustScan like a normal application.
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.9.0'
```
Then we can scan:
```bash
rustscan 192.168.1.0/24 -t 500 -b 1500 -- -A
```
#### To build your own image:
Download the repo:
```bash
git clone https://github.com/RustScan/RustScan.git
```
Ensure you navigate to the download location of the repo:
```bash
cd /path/to/download/RustScan
```
Build away!
```bash
docker build -t <yourimagename> .
```
1. Install Rust You can do this with `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` which I took from the Rust website https://www.rust-lang.org/tools/install
2. `cargo install rustscan` if you want the easiest method possible. Otherwise, to build the .deb file `cargo install cargo-deb`
3. Git clone this repo `git clone https://github.com/brandonskerritt/RustScan`
4. cd RustScan (into the git cloned repo) `cd RustScan`
5. Run `cargo deb`
6. Your .deb file is now located in `target/releases/Debian/`
## 🍺 HomeBrew
**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:
```
brew tap brandonskerritt/rustscan
```
Install it:
```
brew install rustscan
@@ -201,114 +147,70 @@ brew install rustscan
5. Symlink to the binary or something. Whatever you want!
## 🦊 Community Distributions
Here are all of RustScan's distributions, along with some community ones.
Here are all of RustScan's community distributions.
If you maintain a community distribution and want it listed here, leave an issue / pull request / Discord message or however you want to let us know.
- [OpenSuse](https://software.opensuse.org/package/rustscan?search_term=rustscan)
- [Fedora/CentOS](https://copr.fedorainfracloud.org/coprs/atim/rustscan/)
* [OpenSuse](https://software.opensuse.org/package/rustscan?search_term=rustscan)
[![Packaging status](https://repology.org/badge/vertical-allrepos/rustscan.svg)](https://repology.org/project/rustscan/versions)
# 🤸 Usage
```console
$ rustscan -h
Fast Port Scanner built in Rust. WARNING Do not use this program against sensitive infrastructure since the specified
server may not be able to handle this many socket connections at once. - Discord https://discord.gg/GFrQsGy - GitHub
https://github.com/RustScan/RustScan
USAGE:
rustscan [FLAGS] [OPTIONS] [addresses]... [-- <command>...]
FLAGS:
--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 <batch-size> The batch size for port scanning, it increases or slows the speed of scanning.
Depends on the open file limit of your OS. If you do 65535 it will do every port
at the same time. Although, your OS may not support this [default: 4500]
-p, --ports <ports>... A list of comma separed ports to be scanned. Example: 80,443,8080
-r, --range <range> A range of ports with format start-end. Example: 1-1000
--scan-order <scan-order> The order of scanning to be performed. The "serial" option will scan ports in
ascending order while the "random" option will scan ports randomly [default:
serial] [possible values: Serial, Random]
-t, --timeout <timeout> The timeout in milliseconds before a port is assumed to be closed [default: 1500]
-u, --ulimit <ulimit> Automatically ups the ULIMIT with the value you provided
ARGS:
<addresses>... A list of comma separated CIDRs, IPs, or hosts to be scanned
<command>... The Nmap arguments to run. To use the argument -A, end RustScan's args with '-- -A'. Example:
'rustscan -t 1500 127.0.0.1 -- -A -sC'. This command adds -Pn -vvv -p $PORTS automatically to
nmap. For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and
vuln)'\"")
```
rustscan -h
```
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.
```
RustScan 1.2.0
Bee https://github.com/brandonskerritt
Fast Port Scanner built in Rust
WARNING Do not use this program against sensitive infrastructure. The specified server may not be able to handle this
many socket connections at once.
The batch size determines how fast RustScan is. Set it to 65k, and it will scan all 65k ports at the same time. This means at at 65k batch size, RustScan will take TIMEOUT long to scan all ports. Essentially, if timeout is 1000ms, **RustScan can scan in 1 second**.
USAGE:
rustscan [FLAGS] [OPTIONS] <ip> [command]...
FLAGS:
-h, --help Prints help information
-q, --quiet Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
-V, --version Prints version information
OPTIONS:
-T, --timeout <T> The timeout before a port is assumed to be close. In MS. [default: 1500]
-b, --batch <b> Increases speed of scanning. The batch size for port scanning. Depends on your open file limit
of OS. If you do 65535 it will do every port at the same time. Although, your OS may not
support this. [default: 4500]
-u, --ulimit <u> Automatically ups the ULIMIT with the value you provided.
ARGS:
<ip> The IP address to scan
<command>... The Nmap arguments to run. To use the argument -A, end RustScan's args with '-- -A'. To run
EXAMPLE: 'rustscan -T 1500 127.0.0.1 -- -A -sC'. This argument auto runs nmap {your commands}
-vvv -p $PORTS
```
The format is `rustscan -b 500 -T 1500 192.168.0.1` to scan 192.168.0.1 with 500 batch size with a timeout of 1500ms. The timeout is how long RustScan waits for a response until it assumes the port is closed.
The batch size determines how fast RustScan is. Set it to 65k, and it will scan all 65k ports at the same time. This means at at 65k batch size, RustScan will take TIMEOUT long to scan all ports. Essentially, if timeout is 1000ms, **RustScan can scan in 1 second**.
Your operating system may not support this, but it is worth it to play around and see where your open file limit is. Shortly I will be releasing a dockerised version with a much larger open file limit, so this will be possible.
## Configuration file
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:
- `addresses`
- `ports`
- `range`
- `scan_order`
- `command`
- `accessible`
- `quiet`
- `batch-size`
- `timeout`
- `ulimit`
### Format example
```toml
addresses = ["127.0.0.1", "192.168.0.0/30", "www.google.com"]
command = ["-A"]
ports = [80, 443, 8080]
range = { start = 1, end = 10 }
quiet = false
accessible = true
scan_order = "Serial"
batch_size = 1000
timeout = 1000
ulimit = 1000
```
## ⚠️ WARNING
This program, by default, scans 5000 ports at a time (5000 per second).
This program, by default, scans 5000 ports at a time (5000 per second).
This may cause damage to a server, or may make it incredibly obvious you are scanning the server.
Servers as well as individual services can block your IP completely after a huge amount of simultaneous connections/tries, Possibly causing you to see false positives. False positives are open ports at the initial scanning process later found closed when you want to do further scanning, or connect to it.
There are 2 ways to deal with this;
1. Decrease batch size
`rustscan -b 10` will run 10 port scans for 1 second, and then another 10 for 1 second and so on.
`rustscan -b 10` will run 10 port scans for 1 second, and then another 10 for 1 second and so on.
2. Increase timeout
`rustscan -T 5000` will mean RustScan waits 5 seconds until it scans the next ports.
`rustscan -T 5000` will mean RustScan waits 5 seconds until it scans the next ports.
You can also use both of these at the same time, to make it as slow or as fast as you want. A fun favourite is 65535 batch size with 1 second timeout. Theoretically, this scans all 65535 ports in 1 second.
**Please** do not use this tool against sensitive servers. It is designed mainly for Capture the Flag events, not real world servers with sensitive data.
## 🚨 Thread Panicked at Main: Too Many Open Files
## 🚨 Thread Paniced at Main: Too Many Open Files
This is the most common error found in RustScan.
The open file limit is how many open sockets you can have at any given time.
@@ -317,10 +219,9 @@ This limit changes from OS to OS.
RustScan does not automatically create defaults (other than 5000) like Nmap does with their -T1, -T2 system.
By figuring out for yourself the optimal batch size, you will know that RustScan is the most optimised port scanner for your system.
By figuring out for yourself the optimal batch size, you will know that RustScan is the most optimised port scanner for your system.
There are 2 things you can do:
1. Decrease batch size
2. Increase open file limit
@@ -328,7 +229,7 @@ Decreasing batch size slows down the program, so as long as it isn't too drastic
Run these 3 commands:
```bash
```
ulimit -a
ulimit -Hn
ulimit -Sn
@@ -342,42 +243,40 @@ Increasing the open file limit increases speed, but poses danger. Although, **op
To open more, set the ulimit to a higher number:
```bash
```
ulimit -n 5000
```
**Mac OS**
Mac OS has, from what I can tell, a naturally very low open file descriptor limit. The limit for Ubuntu is 8800. The limit for Mac OS is 255!
Mac OS has, from what I can tell, a naturally very low open file descriptor limit. The limit for Ubuntu is 8800. The limit for Mac OS is 255!
In this case, I would say it is safe to increase the open file limit. As most Linux based OS' have limits in the thousands.
In this case, I would say it is safe to increase the open file limit. As most Linux based OS' have limits in the thousands.
Although, if this breaks anything, please don't blame me.
Although, if this breaks anything, please don't blame me.
**Windows Subsystem for Linux**
Windows Subsystem for Linux does not support ulimit (see issue #39).
Windows Subsystem for Linux does not support ulimt (see issue #39).
The best way is to use it on a host computer, in Docker, or in a VM that isn't WSL.
The best way is to use it on a host computer, in Docker, or in a VM that isn't WSL.
**Automatic Ulimit updating**
We are currently working on automatic Ulimit updating. If it is too high, it will lower itself. If it is too low, it will suggest a higher Ulimit. Watch [this issue](https://github.com/RustScan/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/brandonskerritt/RustScan/issues/25) for more.
## 🔌 Nmap Custom Flags
To run your own nmap commands, end the RustScan command with `-- -A` where `--` indicates "end of RustScan flags, please do not parse anything further" and any flags after that will be entered into nmap.
RustScan automatically runs `nmap -vvv -p $PORTS $IP`. To make it run `-A`, execute the command `rustscan 127.0.0.1 -- -A`.
RustScan automatically runs `nmap -vvv -p $PORTS $IP`. To make it run `-A`, execute the command `rustscan 127.0.0.1 -- -A`.
If you want to run commands such as `--script (vuln and safe)`, you will need to enclose it in quotations like so `--script '"(vuln and safe) or default"'`.
## 🎯 Increasing speed / accuracy
- Batch size
* Batch size
This increases speed, by allowing us to process more at once. Something experimental I am working on is changing the open file limit. You can do this manually with `ulimit -n 70000` and then running rustscan with `-b 65535`. This _should_ scan all 65535 ports at the exact same time. But this is extremely experimental.
For non-experimental speed increases, slowly increase the batch size until it no longer gets open ports, or it breaks.
- Accuracy (and some speed)
* Accuracy (and some speed)
To increase accuracy, the easiest way is to increase the timeout. The default is 1.5 seconds, by setting it to 4 seconds (4000) we are telling RustScan "if we do not hear back from a port in 4 seconds, assume it is closed".
@@ -394,21 +293,19 @@ Before contributing, read our [code of conduct](https://github.com/RustScan/Rust
TL;DR if you abuse members of our community you will be **perma-banned** 🤗
RustScan has 2 major labels for GitHub issues you should look at:
- Good First issue
These are issues for newcomers to open source!
[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!
[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)
* Good First issue
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!
[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.
The flow for contributing to open source software is:
- Fork the repo
- Make changes
- Pull request to the repo
* Fork the repo
* Make changes
* Pull request to the repo
And then comment on the issue that you've done.
@@ -419,15 +316,14 @@ If you have any feature suggestions or bugs, leave a GitHub issue. We welcome an
We communicate over Discord. [Click here](https://discord.gg/GFrQsGy) to join our Discord community!
## Rewarding you
I cannot pay you :-( But, I can place your GitHub profile on the README under `#Contributors` as a thank you! :)
Please read the [contributing.md file](contributing.md)
## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -445,23 +341,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://imlonghao.com"><img src="https://avatars1.githubusercontent.com/u/4951333?v=4" width="100px;" alt=""/><br /><sub><b>imlonghao</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/issues?q=author%3Aimlonghao" title="Bug reports">🐛</a> <a href="#maintenance-imlonghao" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/royharoush"><img src="https://avatars3.githubusercontent.com/u/8113056?v=4" width="100px;" alt=""/><br /><sub><b>royharoush</b></sub></a><br /><a href="#ideas-royharoush" title="Ideas, Planning, & Feedback">🤔</a> <a href="#design-royharoush" title="Design">🎨</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Atul9"><img src="https://avatars1.githubusercontent.com/u/3390330?v=4" width="100px;" alt=""/><br /><sub><b>Atul Bhosale</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=Atul9" title="Code">💻</a></td>
<td align="center"><a href="https://tgotwig.me"><img src="https://avatars0.githubusercontent.com/u/30773779?v=4" width="100px;" alt=""/><br /><sub><b>Thomas Gotwig</b></sub></a><br /><a href="#platform-TGotwig" title="Packaging/porting to new platform">📦</a></td>
<td align="center"><a href="https://github.com/remigourdon"><img src="https://avatars3.githubusercontent.com/u/2874133?v=4" width="100px;" alt=""/><br /><sub><b>Rémi Gourdon</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=remigourdon" title="Documentation">📖</a> <a href="https://github.com/RustScan/RustScan/commits?author=remigourdon" title="Code">💻</a></td>
<td align="center"><a href="https://cmnatic.co.uk"><img src="https://avatars3.githubusercontent.com/u/4163116?v=4" width="100px;" alt=""/><br /><sub><b>Ben (CMNatic)</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=cmnatic" title="Code">💻</a> <a href="https://github.com/RustScan/RustScan/commits?author=cmnatic" title="Documentation">📖</a> <a href="#design-cmnatic" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/Ferryistaken"><img src="https://avatars3.githubusercontent.com/u/47927670?v=4" width="100px;" alt=""/><br /><sub><b>Alessandro Ferrari</b></sub></a><br /><a href="#content-Ferryistaken" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/Phenomite"><img src="https://avatars2.githubusercontent.com/u/8285537?v=4" width="100px;" alt=""/><br /><sub><b>Phenomite</b></sub></a><br /><a href="#content-Phenomite" title="Content">🖋</a></td>
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4" width="100px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="#content-SuperSandro2000" title="Content">🖋</a> <a href="https://github.com/RustScan/RustScan/issues?q=author%3ASuperSandro2000" title="Bug reports">🐛</a> <a href="https://github.com/RustScan/RustScan/commits?author=SuperSandro2000" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://swag.lgbt"><img src="https://avatars2.githubusercontent.com/u/25358963?v=4" width="100px;" alt=""/><br /><sub><b>Cass</b></sub></a><br /><a href="#platform-caass" title="Packaging/porting to new platform">📦</a> <a href="https://github.com/RustScan/RustScan/commits?author=caass" title="Code">💻</a> <a href="https://github.com/RustScan/RustScan/issues?q=author%3Acaass" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/niklasmohrin"><img src="https://avatars0.githubusercontent.com/u/47574893?v=4" width="100px;" alt=""/><br /><sub><b>Niklas Mohrin</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=niklasmohrin" title="Documentation">📖</a></td>
<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>
<!-- markdownlint-enable -->
-1004
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -8,15 +8,15 @@ TL;DR if you abuse members of our community you will be **perma-banned** with no
RustScan has 2 major labels for GitHub issues you should look at:
* Good First issue
These are issues for newcomers to open source!
These are issues for newcomers to open source!
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
* Help wanted
These are issues that aren't really for newcomers, but we could still do with help!
These are issues that aren't really for newcomers, but we could still do wiht 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.
The [flow for contributing](https://guides.github.com/introduction/flow/) to open source software is:
The flow for contributing to open source software is:
* Fork the repo
* Make changes
* Pull request to the repo
-6
View File
@@ -1,6 +0,0 @@
127.0.0.1
google.com
example.com
66666666666666.666666666666666666.66666666666666666.6666666666
....
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.radiatorFixtures
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 12 KiB

-89
View File
@@ -1,89 +0,0 @@
//! # Usage
//! ```
//! // Initiate Benchmark vector
//! let mut bm = Benchmark::init();
//! // Start named timer with name
//! let mut example_bench = NamedTimer::start("Example Bench");
//! // Stop named timer
//! example_bench.stop();
//! // Add named timer to Benchmarks
//! bm.push(example_bench);
//! // Print Benchmark Summary
//! info!({}, bm.summary());
//! ```
use std::time::Instant;
/// A Benchmark struct to hold NamedTimers with name, start and end Instants,
#[derive(Debug)]
pub struct Benchmark {
named_timers: Vec<NamedTimer>,
}
impl Benchmark {
pub fn init() -> Self {
Self {
named_timers: Vec::new(),
}
}
pub fn push(&mut self, timer: NamedTimer) {
self.named_timers.push(timer);
}
/// Summary of the benchmarks will destruct the vector,
/// formats every element the same way and return
/// a single String with all the available information
/// for easy printing
pub fn summary(&self) -> String {
let mut summary = String::from("\nRustScan Benchmark Summary");
for timer in &self.named_timers {
if timer.start.is_some() && timer.end.is_some() {
let runtime_secs = timer
.end
.unwrap()
.saturating_duration_since(timer.start.unwrap())
.as_secs_f32();
summary.push_str(&format!("\n{0: <10} | {1: <10}s", timer.name, runtime_secs))
}
}
summary
}
}
/// The purpose of NamedTimer is to hold a name,
/// start Instant and end Instant for a specific timer.
/// The given name will be presented in the benchmark summary,
/// start and end Instants will be used for calculating runtime.
#[derive(Debug)]
pub struct NamedTimer {
name: &'static str,
start: Option<Instant>,
end: Option<Instant>,
}
impl NamedTimer {
pub fn start(name: &'static str) -> Self {
Self {
name: name,
start: Some(Instant::now()),
end: None,
}
}
pub fn end(&mut self) {
self.end = Some(Instant::now());
}
}
#[test]
fn benchmark() {
let mut benchmarks = Benchmark::init();
let mut test_timer = NamedTimer::start("test");
std::thread::sleep(std::time::Duration::from_millis(100));
test_timer.end();
benchmarks.push(test_timer);
benchmarks.push(NamedTimer::start("only_start"));
assert!(benchmarks
.summary()
.contains("\nRustScan Benchmark Summary\ntest | 0."));
assert_ne!(benchmarks.summary().contains("only_start"), true);
}
-329
View File
@@ -1,329 +0,0 @@
use serde_derive::Deserialize;
use std::collections::HashMap;
use std::fs;
use structopt::{clap::arg_enum, StructOpt};
const LOWEST_PORT_NUMBER: u16 = 1;
const TOP_PORT_NUMBER: u16 = 65535;
arg_enum! {
/// Represents the strategy in which the port scanning will run.
/// - Serial will run from start to end, for example 1 to 1_000.
/// - Random will randomize the order in which ports will be scanned.
#[derive(Deserialize, Debug, StructOpt, Clone, PartialEq)]
pub enum ScanOrder {
Serial,
Random,
}
}
/// Represents the range of ports to be scanned.
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct PortRange {
pub start: u16,
pub end: u16,
}
#[cfg(not(tarpaulin_include))]
fn parse_range(input: &str) -> Result<PortRange, String> {
let range = input
.split("-")
.map(|x| x.parse::<u16>())
.collect::<Result<Vec<u16>, std::num::ParseIntError>>();
if range.is_err() {
return Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
));
}
match range.unwrap().as_slice() {
[start, end] => Ok(PortRange {
start: *start,
end: *end,
}),
_ => Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
)),
}
}
#[derive(StructOpt, Debug)]
#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
/// Fast Port Scanner built in Rust.
/// WARNING Do not use this program against sensitive infrastructure since the
/// specified server may not be able to handle this many socket connections at once.
/// - Discord https://discord.gg/GFrQsGy
/// - GitHub https://github.com/RustScan/RustScan
pub struct Opts {
/// A list of comma separated CIDRs, IPs, or hosts to be scanned.
#[structopt(use_delimiter = true)]
pub addresses: Vec<String>,
/// A list of comma separed ports to be scanned. Example: 80,443,8080.
#[structopt(short, long, use_delimiter = true)]
pub ports: Option<Vec<u16>>,
/// A range of ports with format start-end. Example: 1-1000.
#[structopt(short, long, conflicts_with = "ports", parse(try_from_str = parse_range))]
pub range: Option<PortRange>,
/// Whether to ignore the configuration file or not.
#[structopt(short, long)]
pub no_config: bool,
/// Greppable mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
#[structopt(short, long)]
pub greppable: bool,
/// Accessible mode. Turns off features which negatively affect screen readers.
#[structopt(long)]
pub accessible: bool,
/// Turns off Nmap.
#[structopt(long)]
pub no_nmap: bool,
/// The batch size for port scanning, it increases or slows the speed of
/// scanning. Depends on the open file limit of your OS. If you do 65535
/// it will do every port at the same time. Although, your OS may not
/// support this.
#[structopt(short, long, default_value = "4500")]
pub batch_size: u16,
/// The timeout in milliseconds before a port is assumed to be closed.
#[structopt(short, long, default_value = "1500")]
pub timeout: u32,
/// Automatically ups the ULIMIT with the value you provided.
#[structopt(short, long)]
pub ulimit: Option<rlimit::rlim>,
/// The order of scanning to be performed. The "serial" option will
/// scan ports in ascending order while the "random" option will scan
/// ports randomly.
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
pub scan_order: ScanOrder,
/// The Nmap arguments to run.
/// To use the argument -A, end RustScan's args with '-- -A'.
/// Example: 'rustscan -T 1500 127.0.0.1 -- -A -sC'.
/// 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)'\"")
#[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();
if opts.ports.is_none() && opts.range.is_none() {
opts.range = Some(PortRange {
start: LOWEST_PORT_NUMBER,
end: TOP_PORT_NUMBER,
});
}
opts
}
/// Reads the command line arguments into an Opts struct and merge
/// values found within the user configuration file.
pub fn merge(&mut self, config: &Config) {
if !self.no_config {
self.merge_required(&config);
self.merge_optional(&config);
}
}
fn merge_required(&mut self, config: &Config) {
macro_rules! merge_required {
($($field: ident),+) => {
$(
if let Some(e) = &config.$field {
self.$field = e.clone();
}
)+
}
}
merge_required!(addresses, greppable, accessible, batch_size, timeout, scan_order, command);
}
fn merge_optional(&mut self, config: &Config) {
macro_rules! merge_optional {
($($field: ident),+) => {
$(
if config.$field.is_some() {
self.$field = config.$field.clone();
}
)+
}
}
// 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<HashMap<String, u16>>,
range: Option<PortRange>,
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>,
command: Option<Vec<String>>,
}
#[cfg(not(tarpaulin_include))]
impl Config {
/// Reads the configuration file with TOML format and parses it into a
/// Config struct.
///
/// # Format
///
/// addresses = ["127.0.0.1", "127.0.0.1"]
/// ports = [80, 443, 8080]
/// quiet = true
/// scan_order: "Serial"
///
pub fn read() -> Self {
let mut home_dir = match dirs::home_dir() {
Some(dir) => dir,
None => panic!("Could not infer config file path."),
};
home_dir.push(".rustscan.toml");
let mut content = String::new();
if home_dir.exists() {
content = match fs::read_to_string(home_dir) {
Ok(content) => content,
Err(_) => String::new(),
}
}
let config: Config = match toml::from_str(&content) {
Ok(config) => config,
Err(e) => {
println!("Found {} in configuration file.\nAborting scan.\n", e);
std::process::exit(1);
}
};
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::default();
let config = Config::default();
opts.merge(&config);
assert_eq!(opts.addresses, vec![] as Vec<String>);
assert_eq!(opts.greppable, true);
assert_eq!(opts.accessible, false);
assert_eq!(opts.timeout, 0);
assert_eq!(opts.command, vec![] as Vec<String>);
assert_eq!(opts.scan_order, ScanOrder::Serial);
}
#[test]
fn opts_merge_required_arguments() {
let mut opts = Opts::default();
let config = Config::default();
opts.merge_required(&config);
assert_eq!(opts.addresses, config.addresses.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());
assert_eq!(opts.scan_order, config.scan_order.unwrap());
}
#[test]
fn opts_merge_optional_arguments() {
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.range, config.range);
assert_eq!(opts.ulimit, config.ulimit);
}
}
+267 -331
View File
@@ -1,38 +1,17 @@
extern crate shell_words;
mod tui;
mod input;
use input::{Config, Opts, PortRange, ScanOrder};
mod scanner;
use scanner::Scanner;
mod port_strategy;
use port_strategy::PortStrategy;
mod benchmark;
use benchmark::{Benchmark, NamedTimer};
use cidr_utils::cidr::IpCidr;
use colorful::Color;
use colorful::Colorful;
use colored::*;
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::process::{exit, Command};
use std::{net::IpAddr, time::Duration};
use trust_dns_resolver::config::*;
use trust_dns_resolver::Resolver;
use structopt::StructOpt;
extern crate colorful;
extern crate dirs;
// Average value for Ubuntu
const DEFAULT_FILE_DESCRIPTORS_LIMIT: rlimit::rlim = 8000;
// Safest batch size based on experimentation
@@ -41,151 +20,148 @@ const AVERAGE_BATCH_SIZE: rlimit::rlim = 3000;
#[macro_use]
extern crate log;
#[derive(StructOpt, Debug)]
#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
/// Fast Port Scanner built in Rust.
/// WARNING Do not use this program against sensitive infrastructure since the
/// specified server may not be able to handle this many socket connections at once.
/// - Discord https://discord.gg/GFrQsGy
/// - GitHub https://github.com/RustScan/RustScan
struct Opts {
/// The IP address to scan
#[structopt(parse(try_from_str))]
ip: IpAddr,
///Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
#[structopt(short, long)]
quiet: bool,
/// The batch size for port scanning, it increases or slows the speed of
/// scanning. Depends on the open file limit of your OS. If you do 65535
/// it will do every port at the same time. Although, your OS may not
/// support this.
#[structopt(short, long, default_value = "4500")]
batch_size: u32,
/// The timeout in milliseconds before a port is assumed to be closed.
#[structopt(short, long, default_value = "1500")]
timeout: u32,
/// Automatically ups the ULIMIT with the value you provided.
#[structopt(short, long)]
ulimit: Option<rlimit::rlim>,
/// 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)'\"")
command: Vec<String>,
}
#[cfg(not(tarpaulin_include))]
/// Faster Nmap scanning with Rust
/// If you're looking for the actual scanning, check out the module Scanner
fn main() {
// logger
env_logger::init();
let mut benchmarks = Benchmark::init();
let mut rustscan_bench = NamedTimer::start("RustScan");
let mut opts: Opts = Opts::read();
let config = Config::read();
opts.merge(&config);
info!("Starting up");
let opts = Opts::from_args();
info!("Mains() `opts` arguments are {:?}", opts);
debug!("Main() `opts` arguments are {:?}", opts);
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.",
opts.greppable,
opts.accessible
);
std::process::exit(1);
if !opts.quiet {
print_opening();
}
let ulimit: rlimit::rlim = adjust_ulimit_size(&opts);
let batch_size: u16 = infer_batch_size(&opts, ulimit);
let batch_size: u32 = infer_batch_size(&opts, ulimit);
// 65535 + 1 because of 0 indexing
let scanner = Scanner::new(
&ips,
opts.ip,
1,
65535,
batch_size,
Duration::from_millis(opts.timeout.into()),
opts.greppable,
PortStrategy::pick(opts.range, opts.ports, opts.scan_order),
opts.accessible,
opts.quiet,
);
debug!("Scanner finished building: {:?}", scanner);
let mut portscan_bench = NamedTimer::start("Portscan");
let scan_result = block_on(scanner.run());
portscan_bench.end();
benchmarks.push(portscan_bench);
let mut ports_per_ip = HashMap::new();
// prints ports and places them into nmap string
let nmap_str_ports: Vec<String> = scan_result
.into_iter()
.map(|port| port.to_string())
.collect();
for socket in scan_result {
ports_per_ip
.entry(socket.ip())
.or_insert_with(Vec::new)
.push(socket.port());
}
for ip in ips {
if ports_per_ip.contains_key(&ip) {
continue;
}
// If we got here it means the IP was not found within the HashMap, this
// means the scan couldn't find any open ports for it.
let x = format!("Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size.
\n*I used {} batch size, consider lowering it with {} or a comfortable number for your system.
\n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n",
ip,
// if no ports are found, suggest running with less
if nmap_str_ports.is_empty() {
panic!("{} Looks like I didn't find any open ports. This is usually caused by a high batch size.
\n*I used {} batch size, consider lowering to {} with {} or a comfortable number for your system.
\n Alternatively, increase the timeout if your ping is high. Rustscan -T 2000 for 2000 second timeout.", "ERROR".red(),
opts.batch_size,
"'rustscan -b <batch_size> <ip address>'");
warning!(x, opts.greppable, opts.accessible);
(opts.batch_size / 2).to_string().green(),
"'rustscan -b <batch_size> <ip address>'".green());
}
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();
// 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.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(" ")
),
opts.greppable.clone(),
opts.accessible.clone()
);
// Runs the nmap command and spawns it as a process.
let mut child = Command::new("nmap")
.args(&nmap_args)
.spawn()
.expect("failed to execute nmap process");
child.wait().expect("failed to wait on nmap process");
// Tells the user we are now switching to Nmap
if !opts.quiet {
println!("{}", "Starting nmap.".blue(),);
}
// To use the runtime benchmark, run the process as: RUST_LOG=info ./rustscan
nmap_bench.end();
benchmarks.push(nmap_bench);
rustscan_bench.end();
benchmarks.push(rustscan_bench);
debug!("Benchmarks raw {:?}", benchmarks);
info!("{}", benchmarks.summary());
// nmap port style is 80,443. Comma seperated with no spaces.
let ports_str = nmap_str_ports.join(",");
// if quiet mode is on, return ports and exit
if opts.quiet {
println!("{}", ports_str);
exit(1);
}
let addr = opts.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, opts.ip.is_ipv6());
if !opts.quiet {
println!("The Nmap command to be run is {}", &nmap_args.join(" "));
}
// Runs the nmap command and spawns it as a process.
let mut child = Command::new("nmap")
.args(&nmap_args)
.spawn()
.expect("failed to execute nmap process");
child.wait().expect("failed to wait on nmap process");
}
/// Prints the opening title of RustScan
fn print_opening(opts: &Opts) {
debug!("Printing opening");
let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
Faster Nmap scanning with Rust."#;
println!("{}", s.gradient(Color::Green).bold());
let info = r#"________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------"#;
println!("{}", info.gradient(Color::Yellow).bold());
funny_opening!();
fn print_opening() {
info!("Printing opening");
let s = "
_____ _ _____
| __ \\ | | / ____|
| |__) | _ ___| |_| (___ ___ __ _ _ __
| _ / | | / __| __|\\___ \\ / __/ _` | '_ \\
| | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |
|_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|
Faster nmap scanning with rust.";
println!("{}\n", s.green());
let mut home_dir = match dirs::home_dir() {
Some(dir) => dir,
None => panic!("Could not infer config file path."),
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."),
};
home_dir.push(".rustscan.toml");
detail!(
format!("The config file is expected to be at {:?}", home_dir),
opts.greppable,
opts.accessible
println!(
"{} {:?}\n",
"The config file is expected to be at".yellow(),
config_path
);
}
#[cfg(not(tarpaulin_include))]
@@ -196,6 +172,7 @@ fn build_nmap_arguments<'a>(
is_ipv6: bool,
) -> Vec<&'a str> {
let mut arguments: Vec<&str> = user_args.iter().map(AsRef::as_ref).collect();
arguments.push("-A");
arguments.push("-vvv");
if is_ipv6 {
@@ -209,115 +186,17 @@ 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 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),
_ => {
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();
match setrlimit(Resource::NOFILE, limit, limit) {
Ok(_) => {
detail!(
format!("Automatically increasing ulimit value to {}.", limit),
opts.greppable,
opts.accessible
);
}
Err(_) => {
warning!(
"ERROR. Failed to set ulimit value.",
opts.greppable,
opts.accessible
);
if !opts.quiet {
println!("\nAutomatically increasing ulimit value to {}.\n", limit);
}
}
Err(_) => println!("{}", "ERROR. Failed to set ulimit value.".red()),
}
}
@@ -326,14 +205,14 @@ fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
rlim
}
fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u32 {
let mut batch_size: rlimit::rlim = opts.batch_size.into();
// 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.greppable, opts.accessible
);
if !opts.quiet {
println!("{}", "WARNING: Your file description limit is lower than the provided batch size. Please considering upping this (instructions in our README). NOTE: this may be dangerous and may cause harm to sensitive servers. Automatically reducing the batch Size to match your system's limit, this process isn't harmful but reduces speed.".red());
}
// When the OS supports high file limits like 8000, but the user
// selected a batch size higher than this we should reduce it to
@@ -342,8 +221,8 @@ 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'. ", opts.greppable, opts.accessible);
info!("Halving batch_size because ulimit is smaller than average batch size");
println!("{}", "WARNING. Your open file description limit is smaller than expected. You can increase the ulimit with the '-u' flag like '-u 5000' to get default size. Or, use the Docker image. If you do not increase ulimit your RustScan speeds will be much slower in comparison to a normal ulimit.".red());
batch_size = ulimit / 2
} else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT {
info!("Batch size is now average batch size");
@@ -355,124 +234,181 @@ 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.greppable, opts.accessible);
if !opts.quiet {
println!(
"Your file descriptor limit is higher than the batch size. You can potentially increase the speed by increasing the batch size, but this may cause harm to sensitive servers. Your limit is {}, try batch size {}.\n",
ulimit,
ulimit - 1
);
}
}
batch_size as u16
println!("The batch size is {}", batch_size);
batch_size as u32
}
#[cfg(test)]
mod tests {
use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts};
use std::net::Ipv4Addr;
use super::Scanner;
use crate::{adjust_ulimit_size, build_nmap_arguments, infer_batch_size, print_opening, Opts};
use async_std::task::block_on;
use std::{net::IpAddr, str::FromStr, time::Duration};
#[test]
fn scanner_runs() {
// Makes sure te program still runs and doesn't panic
let addr = match "127.0.0.1".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 65535, 100, Duration::from_millis(100), true);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn ipv6_scanner_runs() {
// Makes sure te program still runs and doesn't panic
let addr = match "::1".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 65535, 100, Duration::from_millis(100), false);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn quad_zero_scanner_runs() {
let addr = match "0.0.0.0".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 1000, 100, Duration::from_millis(500), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn zero_ports_no_return_no_panic() {
let addr = match "0.0.0.0".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 1, 100, Duration::from_millis(50), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn backwards_ports_scanner_runs() {
let addr = match "0.0.0.0".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 10, 1, 100, Duration::from_millis(50), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn google_dns_runs() {
let addr = match "8.8.8.8".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 400, 445, 100, Duration::from_millis(1500), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn infer_ulimit_lowering_no_panic() {
// this test is because of a bug where Mac OS didn't automatically lower ulimit
let addr = match "8.8.8.8".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
// mac should have this automatically scaled down
let scanner = Scanner::new(addr, 400, 600, 10_000, Duration::from_millis(1500), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn batch_size_lowered() {
let mut opts = Opts::default();
opts.batch_size = 50_000;
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
command: Vec::new(),
};
let batch_size = infer_batch_size(&opts, 120);
assert!(batch_size < opts.batch_size);
assert!(batch_size < 50_000);
}
#[test]
fn batch_size_lowered_average_size() {
let mut opts = Opts::default();
opts.batch_size = 50_000;
let batch_size = infer_batch_size(&opts, 9_000);
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
command: Vec::new(),
};
let batch_size = infer_batch_size(&opts, 9000);
assert!(batch_size == 3_000);
assert!(batch_size == 3000);
}
#[test]
fn batch_size_equals_ulimit_lowered() {
// because ulimit and batch size are same size, batch size is lowered
// to ULIMIT - 100
let mut opts = Opts::default();
opts.batch_size = 50_000;
let batch_size = infer_batch_size(&opts, 5_000);
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
command: Vec::new(),
};
let batch_size = infer_batch_size(&opts, 5000);
assert!(batch_size == 4_900);
assert!(batch_size == 4900);
}
#[test]
fn batch_size_adjusted_2000() {
// ulimit == batch_size
let mut opts = Opts::default();
opts.batch_size = 50_000;
opts.ulimit = Some(2_000);
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
command: Vec::new(),
};
let batch_size = adjust_ulimit_size(&opts);
assert!(batch_size == 2_000);
assert!(batch_size == 2000);
}
#[test]
fn test_print_opening_no_panic() {
let mut opts = Opts::default();
opts.ulimit = Some(2_000);
// print opening should not panic
print_opening(&opts);
// print opening should not paniic
print_opening();
assert!(1 == 1);
}
#[test]
fn test_high_ulimit_no_greppable_mode() {
let mut opts = Opts::default();
opts.batch_size = 10;
opts.greppable = false;
fn test_high_ulimit_no_quiet_mode() {
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
quiet: false,
batch_size: 10,
timeout: 1000,
ulimit: None,
command: Vec::new(),
};
let batch_size = infer_batch_size(&opts, 1000000);
let batch_size = infer_batch_size(&opts, 1_000_000);
assert!(batch_size == opts.batch_size);
}
#[test]
fn parse_correct_addresses() {
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!(
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_host_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["google.com".to_owned()];
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 1);
}
#[test]
fn parse_correct_and_incorrect_addresses() {
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),]);
}
#[test]
fn parse_incorrect_addresses() {
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);
assert!(1 == 1);
}
}
-139
View File
@@ -1,139 +0,0 @@
mod range_iterator;
use super::{PortRange, ScanOrder};
use rand::seq::SliceRandom;
use rand::thread_rng;
use range_iterator::RangeIterator;
/// Represents options of port scanning.
///
/// Right now all these options involve ranges, but in the future
/// it will also contain custom lists of ports.
#[derive(Debug)]
pub enum PortStrategy {
Manual(Vec<u16>),
Serial(SerialRange),
Random(RandomRange),
}
impl PortStrategy {
pub fn pick(range: Option<PortRange>, ports: Option<Vec<u16>>, order: ScanOrder) -> Self {
match order {
ScanOrder::Serial if ports.is_none() => {
let range = range.as_ref().unwrap();
PortStrategy::Serial(SerialRange {
start: range.start,
end: range.end,
})
}
ScanOrder::Random if ports.is_none() => {
let range = range.as_ref().unwrap();
PortStrategy::Random(RandomRange {
start: range.start,
end: range.end,
})
}
ScanOrder::Serial => PortStrategy::Manual(ports.unwrap()),
ScanOrder::Random => {
let mut rng = thread_rng();
let mut ports = ports.unwrap();
ports.shuffle(&mut rng);
PortStrategy::Manual(ports)
}
}
}
pub fn order(&self) -> Vec<u16> {
match self {
PortStrategy::Manual(ports) => ports.to_vec(),
PortStrategy::Serial(range) => range.generate(),
PortStrategy::Random(range) => range.generate(),
}
}
}
/// Trait associated with a port strategy. Each PortStrategy must be able
/// to generate an order for future port scanning.
trait RangeOrder {
fn generate(&self) -> Vec<u16>;
}
/// As the name implies SerialRange will always generate a vector in
/// ascending order.
#[derive(Debug)]
pub struct SerialRange {
start: u16,
end: u16,
}
impl RangeOrder for SerialRange {
fn generate(&self) -> Vec<u16> {
(self.start..self.end).collect()
}
}
/// As the name implies RandomRange will always generate a vector with
/// a random order. This vector is built following the LCG algorithm.
#[derive(Debug)]
pub struct RandomRange {
start: u16,
end: u16,
}
impl RangeOrder for RandomRange {
// Right now using RangeIterator and generating a range + shuffling the
// vector is pretty much the same. The advantages of it will come once
// we have to generate different ranges for different IPs without storing
// actual vectors.
//
// Another benefit of RangeIterator is that it always generate a range with
// a certain distance between the items in the Array. The chances of having
// port numbers close to each other are pretty slim due to the way the
// algorithm works.
fn generate(&self) -> Vec<u16> {
RangeIterator::new(self.start.into(), self.end.into()).collect()
}
}
#[cfg(test)]
mod tests {
use super::PortStrategy;
use crate::{PortRange, ScanOrder};
#[test]
fn serial_strategy_with_range() {
let range = PortRange { start: 1, end: 100 };
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Serial);
let result = strategy.order();
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
}
#[test]
fn random_strategy_with_range() {
let range = PortRange { start: 1, end: 100 };
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let mut result = strategy.order();
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_ne!(expected_range, result);
result.sort();
assert_eq!(expected_range, result);
}
#[test]
fn serial_strategy_with_ports() {
let strategy = PortStrategy::pick(None, Some(vec![80, 443]), ScanOrder::Serial);
let result = strategy.order();
assert_eq!(vec![80, 443], result);
}
#[test]
fn random_strategy_with_ports() {
let strategy = PortStrategy::pick(None, Some((1..10).collect()), ScanOrder::Random);
let mut result = strategy.order();
let expected_range = (1..10).into_iter().collect::<Vec<u16>>();
assert_ne!(expected_range, result);
result.sort();
assert_eq!(expected_range, result);
}
}
-129
View File
@@ -1,129 +0,0 @@
use gcd::Gcd;
use rand::Rng;
pub struct RangeIterator {
active: bool,
normalized_end: u32,
normalized_first_pick: u32,
normalized_pick: u32,
actual_start: u32,
step: u32,
}
/// An iterator that follows the `Linear Congruential Generator` algorithm.
///
/// For more information: https://en.wikipedia.org/wiki/Linear_congruential_generator
impl RangeIterator {
/// Receives the the start and end of a range and normalize
/// these values before selecting a coprime for the end of the range
/// which will server as the step for the algorithm.
///
/// For example, the range `1000-2500` will be normalized to `0-1500`
/// before going through the algorithm.
pub fn new(start: u32, end: u32) -> Self {
let normalized_end = end - start;
let step = pick_random_coprime(normalized_end);
// Randomly choose a number within the range to be the first
// and assign it as a pick.
let mut rng = rand::thread_rng();
let normalized_first_pick = rng.gen_range(0, normalized_end);
Self {
active: true,
normalized_end,
step,
normalized_first_pick,
normalized_pick: normalized_first_pick,
actual_start: start,
}
}
}
impl Iterator for RangeIterator {
type Item = u16;
// The next step is always bound by the formula: N+1 = (N + STEP) % TOP_OF_THE_RANGE
// It will only stop once we generate a number equal to the first generated number.
fn next(&mut self) -> Option<Self::Item> {
if !self.active {
return None;
}
let current_pick = self.normalized_pick;
let next_pick = (current_pick + self.step) % self.normalized_end;
// If the next pick is equal to the first pick this means that
// we have iterated through the entire range.
if next_pick == self.normalized_first_pick {
self.active = false;
}
self.normalized_pick = next_pick;
Some((self.actual_start + current_pick) as u16)
}
}
/// The probability that two random integers are coprime to one another
/// works out to be around 61%, given that we can safely pick a random
/// number and test it. Just in case we are having a bad day and we cannot
/// pick a coprime number after 10 tries we just return "end - 1" which
/// is guaranteed to be a coprime, but won't provide ideal randomization.
///
/// We pick between "lower_range" and "upper_range" since values too close to
/// the boundaries, which in these case are the "start" and "end" arguments
/// would also provide non-ideal randomization as discussed on the paragraph
/// above.
fn pick_random_coprime(end: u32) -> u32 {
let range_boundary = end / 4;
let lower_range = range_boundary;
let upper_range = end - range_boundary;
let mut rng = rand::thread_rng();
let mut candidate = rng.gen_range(lower_range, upper_range);
for _ in 0..10 {
if end.gcd(candidate) == 1 {
return candidate;
} else {
candidate = rng.gen_range(lower_range, upper_range);
}
}
end - 1
}
#[cfg(test)]
mod tests {
use super::RangeIterator;
#[test]
fn range_iterator_iterates_through_the_entire_range() {
let result = generate_sorted_range(1, 10);
let expected_range = (1..10).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1, 100);
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1, 1000);
let expected_range = (1..1000).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1, 65_535);
let expected_range = (1..65_535).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1000, 2000);
let expected_range = (1000..2000).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
}
fn generate_sorted_range(start: u32, end: u32) -> Vec<u16> {
let range = RangeIterator::new(start, end);
let mut result = range.into_iter().collect::<Vec<u16>>();
result.sort();
result
}
}
+139
View File
@@ -0,0 +1,139 @@
use async_std::io;
use async_std::net::TcpStream;
use async_std::prelude::*;
use colored::*;
use futures::stream::FuturesUnordered;
use std::time::Duration;
use std::{
io::ErrorKind,
net::{IpAddr, Shutdown, SocketAddr},
};
/// The class for the scanner
/// Host is data type IpAddr and is the host address
/// start & end is where the port scan starts and ends
/// batch_size is how many ports at a time should be scanned
/// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration.
/// Quiet is whether or not RustScan should print things, or wait until the end to print only open ports.
#[cfg(not(tarpaulin_include))]
pub struct Scanner {
host: IpAddr,
start: u16,
end: u16,
batch_size: u32,
timeout: Duration,
quiet: bool,
}
impl Scanner {
pub fn new(
host: IpAddr,
start: u16,
end: u16,
batch_size: u32,
timeout: Duration,
quiet: bool,
) -> Self {
Self {
host: host.to_owned(),
start,
end,
batch_size,
timeout,
quiet,
}
}
/// Runs scan_range with chunk sizes
/// If you want to run RustScan normally, this is the entry point used
/// Returns all open ports as Vec<u16>
pub async fn run(&self) -> Vec<u16> {
let ports: Vec<u16> = (self.start..self.end).collect();
let mut open_ports: std::vec::Vec<u16> = Vec::new();
// TODO change this to port size
// to fix bug when we introduce custom port ranges
for range in ports.chunks(self.batch_size as usize) {
let mut ports = self.scan_range(range).await;
open_ports.append(&mut ports);
}
open_ports
}
/// Given a range of ports, scan them all.
/// Returns a vector of open ports.
async fn scan_range(&self, range: &[u16]) -> Vec<u16> {
let mut ftrs = FuturesUnordered::new();
for port in range {
ftrs.push(self.scan_port(*port));
}
let mut open_ports: Vec<u16> = Vec::new();
while let Some(result) = ftrs.next().await {
match result {
Ok(port) => open_ports.push(port),
_ => {}
}
}
open_ports
}
/// Given a port, scan it.
/// Turns the address into a SocketAddr
/// Deals with the <result> type
/// If it experiences error ErrorKind::Other then too many files are open and it Panics!
/// ese any other error, it returns the error in Result as a string
/// If no errors occur, it returns the port number in Result to signify the port is open.
/// This function mainly deals with the logic of Results handling.
/// # Example
///
/// self.scan_port(10:u16)
///
/// Note: `self` must contain `self.host`.
async fn scan_port(&self, port: u16) -> io::Result<u16> {
let addr = SocketAddr::new(self.host, port);
// println!("{:?}", addr);
match self.connect(addr).await {
Ok(x) => {
// match stream_result.shutdown(Shutdown::Both)
info!("Shutting down stream");
match x.shutdown(Shutdown::Both) {
_ => {}
}
if !self.quiet {
println!("Open {}", port.to_string().purple());
}
// if connection successful
// shut down stream
// return port
Ok(port)
}
Err(e) => match e.kind() {
ErrorKind::Other => {
panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
}
_ => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
},
}
}
/// Performs the connection to the socket with timeout
/// # Example
///
/// let port: u16 = 80
/// // Host is an IpAddr type
/// let host = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
/// let addr = SocketAddr::new(host, port)
/// self.connect(addr)
/// // returns Result which is either Ok(stream) for port is open, or Er for port is closed.
/// // Timeout occurs after self.timeout seconds
///
async fn connect(&self, addr: SocketAddr) -> io::Result<TcpStream> {
let stream =
io::timeout(self.timeout, async move { TcpStream::connect(addr).await }).await?;
info!("Returning okay from connect");
Ok(stream)
}
}
-235
View File
@@ -1,235 +0,0 @@
use super::PortStrategy;
mod socket_iterator;
use socket_iterator::SocketIterator;
use async_std::io;
use async_std::net::TcpStream;
use async_std::prelude::*;
use colored::*;
use futures::stream::FuturesUnordered;
use std::{
io::ErrorKind,
net::{IpAddr, Shutdown, SocketAddr},
time::Duration,
};
/// The class for the scanner
/// IP is data type IpAddr and is the IP address
/// start & end is where the port scan starts and ends
/// batch_size is how many ports at a time should be scanned
/// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration.
/// Quiet is whether or not RustScan should print things, or wait until the end to print only open ports.
#[cfg(not(tarpaulin_include))]
#[derive(Debug)]
pub struct Scanner {
ips: Vec<IpAddr>,
batch_size: u16,
timeout: Duration,
greppable: bool,
port_strategy: PortStrategy,
accessible: bool,
}
impl Scanner {
pub fn new(
ips: &[IpAddr],
batch_size: u16,
timeout: Duration,
greppable: bool,
port_strategy: PortStrategy,
accessible: bool,
) -> Self {
Self {
batch_size,
timeout,
greppable,
port_strategy,
ips: ips.iter().map(|ip| ip.to_owned()).collect(),
accessible,
}
}
/// Runs scan_range with chunk sizes
/// If you want to run RustScan normally, this is the entry point used
/// Returns all open ports as Vec<u16>
pub async fn run(&self) -> Vec<SocketAddr> {
let ports: Vec<u16> = self.port_strategy.order();
let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports);
let mut open_sockets: Vec<SocketAddr> = Vec::new();
let mut ftrs = FuturesUnordered::new();
for _ in 0..self.batch_size {
if let Some(socket) = socket_iterator.next() {
ftrs.push(self.scan_socket(socket));
} else {
break;
}
}
debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ",
self.batch_size,
self.ips.len(),
&ports.len(),
(self.ips.len() * &ports.len()));
while let Some(result) = ftrs.next().await {
if let Some(socket) = socket_iterator.next() {
ftrs.push(self.scan_socket(socket));
}
match result {
Ok(socket) => open_sockets.push(socket),
_ => {}
}
}
debug!("Open Sockets found: {:?}", &open_sockets);
open_sockets
}
/// Given a port, scan it.
/// Turns the address into a SocketAddr
/// Deals with the <result> type
/// If it experiences error ErrorKind::Other then too many files are open and it Panics!
/// ese any other error, it returns the error in Result as a string
/// If no errors occur, it returns the port number in Result to signify the port is open.
/// This function mainly deals with the logic of Results handling.
/// # Example
///
/// self.scan_port(10:u16)
///
/// Note: `self` must contain `self.ip`.
async fn scan_socket(&self, socket: SocketAddr) -> io::Result<SocketAddr> {
match self.connect(socket).await {
Ok(x) => {
debug!(
"Connection was successful, shutting down stream {}",
&socket
);
match x.shutdown(Shutdown::Both) {
Err(e) => debug!("Shutdown stream error {}", &e),
_ => {}
}
if !self.greppable {
if self.accessible {
println!("Open {}", socket.to_string());
} else {
println!("Open {}", socket.to_string().purple());
}
}
Ok(socket)
}
Err(e) => match e.kind() {
ErrorKind::Other => {
if e.to_string().contains("No route to host")
|| e.to_string().contains("Network is unreachable")
{
debug!("Socket connect error: {} {}", &e.to_string(), &socket);
Err(io::Error::new(io::ErrorKind::Other, e.to_string()))
} else {
debug!("Socket connect error: {} {}", &e.to_string(), &socket);
panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
}
}
_ => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
},
}
}
/// Performs the connection to the socket with timeout
/// # Example
///
/// let port: u16 = 80
/// // ip is an IpAddr type
/// let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
/// let socket = SocketAddr::new(ip, port);
/// self.connect(socket)
/// // returns Result which is either Ok(stream) for port is open, or Er for port is closed.
/// // Timeout occurs after self.timeout seconds
///
async fn connect(&self, socket: SocketAddr) -> io::Result<TcpStream> {
let stream = io::timeout(
self.timeout,
async move { TcpStream::connect(socket).await },
)
.await?;
Ok(stream)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{PortRange, ScanOrder};
use async_std::task::block_on;
use std::{net::IpAddr, time::Duration};
#[test]
fn scanner_runs() {
// Makes sure the program still runs and doesn't panic
let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 1,
end: 1_000,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn ipv6_scanner_runs() {
// Makes sure the program still runs and doesn't panic
let addrs = vec!["::1".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 1,
end: 1_000,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn quad_zero_scanner_runs() {
let addrs = vec!["0.0.0.0".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 1,
end: 1_000,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn google_dns_runs() {
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 400,
end: 445,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn infer_ulimit_lowering_no_panic() {
// Test behaviour on MacOS where ulimit is not automatically lowered
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
// mac should have this automatically scaled down
let range = PortRange {
start: 400,
end: 600,
};
let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true);
block_on(scanner.run());
assert_eq!(1, 1);
}
}
-75
View File
@@ -1,75 +0,0 @@
use itertools::{iproduct, Product};
use std::net::{IpAddr, SocketAddr};
pub struct SocketIterator<'s> {
// 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
/// for each IP and port pair until all of these combinations are exhausted.
/// The goal of this iterator is to go over every IP and port combination
/// wihout generating a big memory footprint. The alternative would be
/// 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 {
product_it: iproduct!(ports_it, ips_it),
}
}
}
impl<'s> Iterator for SocketIterator<'s> {
type Item = SocketAddr;
/// Returns a socket based on the combination of one of the provided
/// IPs and ports or None when these combinations are exhausted. Every
/// IP will have the same port until a port is incremented.
///
/// let it = SocketIterator::new(&["127.0.0.1", "192.168.0.1"], &[80, 443]);
/// it.next(); // 127.0.0.1:80
/// it.next(); // 192.168.0.1:80
/// it.next(); // 127.0.0.1:443
/// it.next(); // 192.168.0.1:443
/// it.next(); // None
fn next(&mut self) -> Option<Self::Item> {
match self.product_it.next() {
None => None,
Some((port, ip)) => Some(SocketAddr::new(*ip, *port)),
}
}
}
#[cfg(test)]
mod tests {
use super::SocketIterator;
use std::net::{IpAddr, SocketAddr};
#[test]
fn goes_through_every_ip_port_combination() {
let addrs = vec![
"127.0.0.1".parse::<IpAddr>().unwrap(),
"192.168.0.1".parse::<IpAddr>().unwrap(),
];
let ports: Vec<u16> = vec![22, 80, 443];
let mut it = SocketIterator::new(&addrs, &ports);
assert_eq!(Some(SocketAddr::new(addrs[0], ports[0])), it.next());
assert_eq!(Some(SocketAddr::new(addrs[1], ports[0])), it.next());
assert_eq!(Some(SocketAddr::new(addrs[0], ports[1])), it.next());
assert_eq!(Some(SocketAddr::new(addrs[1], ports[1])), it.next());
assert_eq!(Some(SocketAddr::new(addrs[0], ports[2])), it.next());
assert_eq!(Some(SocketAddr::new(addrs[1], ports[2])), it.next());
assert_eq!(None, it.next());
}
}
-81
View File
@@ -1,81 +0,0 @@
/// Terminal User Interface Module for RustScan
/// Defines macros to use
#[macro_export]
macro_rules! warning {
($name:expr) => {
use ansi_term::Colour::Red;
println!("{} {}", Red.bold().paint("[!]"), $name);
};
($name:expr, $greppable:expr, $accessible:expr) => {
use ansi_term::Colour::Red;
// 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);
}
}
};
}
#[macro_export]
macro_rules! detail {
($name:expr) => {
use ansi_term::Colour::Blue;
println!("{} {}", Blue.bold().paint("[~]"), $name);
};
($name:expr, $greppable:expr, $accessible:expr) => {
use ansi_term::Colour::Blue;
// 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) => {
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]
macro_rules! funny_opening {
// prints a funny quote / opening
() => {
use rand::seq::SliceRandom;
let quotes = vec![
"Nmap? More like slowmap.🐢",
"🌍HACK THE PLANET🌍",
"Real hackers hack time ⌛",
"Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan",
"😵 https://admin.tryhackme.com",
];
let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap();
println!("{}\n", random_quote);
// println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
};
}