mirror of
https://github.com/bee-san/RustScan.git
synced 2026-11-07 09:46:04 -10:00
Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcc558cb43 | ||
|
|
bb2973049c | ||
|
|
a26ea80036 | ||
|
|
2643a1d5e1 | ||
|
|
b9337d8267 | ||
|
|
e4a097c7dc | ||
|
|
15290b2275 | ||
|
|
6993fb2037 | ||
|
|
a311726486 | ||
|
|
030e9708f9 | ||
|
|
7f80dd1678 | ||
|
|
f5a587accf | ||
|
|
1da08dcb9a | ||
|
|
6ca22d697c | ||
|
|
ac38ca591d | ||
|
|
f2d9a2e1fa | ||
|
|
e3c05afabb | ||
|
|
c466e0641e | ||
|
|
331efa5011 | ||
|
|
fa1ddc9bba | ||
|
|
170f57c0a3 | ||
|
|
6b6b2f267c | ||
|
|
2287965c5c | ||
|
|
1ec8ab412e | ||
|
|
598b5dfd10 | ||
|
|
1a5ff5bb4b | ||
|
|
10b3f82bb7 | ||
|
|
0e582fdd16 | ||
|
|
4c933b39a1 | ||
|
|
a3495b305b | ||
|
|
4bec0a8ea8 | ||
|
|
e86b86d0e0 | ||
|
|
43d3a1ed5a | ||
|
|
0f65753dcb | ||
|
|
2ab7191a65 | ||
|
|
d7c07c4c84 | ||
|
|
478851ac4c | ||
|
|
1c7b080d6f | ||
|
|
7dd9352bae | ||
|
|
d3fdc0d796 | ||
|
|
77853ad122 | ||
|
|
71f21aa259 | ||
|
|
bcdb0df1d9 | ||
|
|
5b00fa1727 | ||
|
|
dde7d96256 | ||
|
|
405f05d42c | ||
|
|
9e1cb60a05 | ||
|
|
8045af8cd2 | ||
|
|
221cf8694f | ||
|
|
bd32f4ead4 | ||
|
|
8625f939bf | ||
|
|
8f608eaf99 | ||
|
|
f9f2bf6cf8 | ||
|
|
6fb1196244 | ||
|
|
61c43dd5d0 | ||
|
|
075025113d | ||
|
|
e3645b5c81 | ||
|
|
a444ab66e1 |
No files matched your search
@@ -0,0 +1,29 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: anyhow
|
||||
versions:
|
||||
- 1.0.38
|
||||
- 1.0.39
|
||||
- dependency-name: serde
|
||||
versions:
|
||||
- 1.0.123
|
||||
- dependency-name: futures
|
||||
versions:
|
||||
- 0.3.12
|
||||
- dependency-name: rlimit
|
||||
versions:
|
||||
- 0.5.3
|
||||
@@ -1,10 +0,0 @@
|
||||
name: Check Markdown links
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
@@ -1,70 +0,0 @@
|
||||
on: [push, pull_request]
|
||||
|
||||
name: Continuous integration
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
|
||||
test:
|
||||
name: Test Suite
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
|
||||
fmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- run: rustup component add rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
clippy:
|
||||
name: Clippy linting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: clippy
|
||||
override: true
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
links:
|
||||
name: markdown-link-check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
@@ -0,0 +1,92 @@
|
||||
name: Build
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: '00 01 * * *'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run cargo check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
|
||||
test:
|
||||
name: Test Suite
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run cargo test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
|
||||
lints:
|
||||
name: Lints
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Run cargo clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
- name: Run rustdoc lints
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --workspace --all-features --no-deps --document-private-items
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
password: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
username: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: rustscan/rustscan
|
||||
flavor: latest=true
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: ${{ contains(fromJson('["push", "release"]'), github.event_name) }} # Publish to docker registry only on push event or new release.
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -16,3 +16,4 @@ jobs:
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
with:
|
||||
use-quiet-mode: 'yes'
|
||||
config-file: './.github/workflows/mlc_config.json'
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"_comment": "Config for markdown-link-check. Resolves issue #540.",
|
||||
"ignorePatterns": [
|
||||
{
|
||||
"pattern": "^https://github.com/RustScan/RustScan/workflows/Continuous%20integration/badge.svg"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://crates.io/crates/rustscan/"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://bees.substack.com/p/making-hacking-accessible"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- '.github/workflows/release.yml'
|
||||
|
||||
env:
|
||||
BIN_NAME: rustscan
|
||||
PROJECT_NAME: rustscan
|
||||
REPO_NAME: RustScan/RustScan
|
||||
# BREW_TAP: RustScan/homebrew-tap
|
||||
|
||||
jobs:
|
||||
dist:
|
||||
name: Dist
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false # don't fail other jobs if one fails
|
||||
matrix:
|
||||
build: [x86_64-linux, x86_64-macos, x86_64-windows] #, x86_64-win-gnu, win32-msvc
|
||||
include:
|
||||
- build: x86_64-linux
|
||||
os: ubuntu-20.04
|
||||
rust: stable
|
||||
target: x86_64-unknown-linux-gnu
|
||||
cross: false
|
||||
- build: x86_64-macos
|
||||
os: macos-latest
|
||||
rust: stable
|
||||
target: x86_64-apple-darwin
|
||||
cross: false
|
||||
- build: x86_64-windows
|
||||
os: windows-2019
|
||||
rust: stable
|
||||
target: x86_64-pc-windows-msvc
|
||||
cross: false
|
||||
# - build: aarch64-linux
|
||||
# os: ubuntu-20.04
|
||||
# rust: stable
|
||||
# target: aarch64-unknown-linux-gnu
|
||||
# - build: aarch64-macos
|
||||
# os: macos-latest
|
||||
# rust: stable
|
||||
# target: aarch64-apple-darwin
|
||||
# - build: x86_64-win-gnu
|
||||
# os: windows-2019
|
||||
# rust: stable-x86_64-gnu
|
||||
# target: x86_64-pc-windows-gnu
|
||||
# - build: win32-msvc
|
||||
# os: windows-2019
|
||||
# rust: stable
|
||||
# target: i686-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- name: Set Git config. (windows only)
|
||||
if: contains(matrix.build, 'windows') # Windows is missing some dependencies
|
||||
run: |
|
||||
git config --global pack.windowMemory "100m"
|
||||
git config --global pack.packSizeLimit "100m"
|
||||
git config --global pack.threads "1"
|
||||
git config --global pack.deltaCacheSize "512m"
|
||||
|
||||
- name: Install python 3.10 for windows
|
||||
if: contains(matrix.build, 'windows') # Windows is missing some dependencies
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install ${{ matrix.rust }} toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
|
||||
- name: Run cargo test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.cross }}
|
||||
command: test
|
||||
args: --release --locked --target ${{ matrix.target }}
|
||||
|
||||
- name: Build release binary
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.cross }}
|
||||
command: build
|
||||
args: --release --locked --target ${{ matrix.target }}
|
||||
|
||||
- name: Calculate tag name
|
||||
if: contains(matrix.build, 'linux')
|
||||
run: |
|
||||
name=dev
|
||||
if [[ ${GITHUB_REF} =~ refs/tags/[0-9]+.[0-9]+.[0-9]+ ]]; then
|
||||
name=${GITHUB_REF#refs/tags/}
|
||||
fi
|
||||
echo ::set-output name=val::$name
|
||||
echo TAG=$name >> $GITHUB_ENV
|
||||
id: tagname
|
||||
|
||||
- name: Build deb package (linux only)
|
||||
if: contains(matrix.build, 'linux')
|
||||
run: |
|
||||
cargo install cargo-deb
|
||||
cargo deb --target ${{ matrix.target }} --deb-version ${TAG}
|
||||
|
||||
- name: Build archive
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir dist
|
||||
if [[ "${{ matrix.build }}" =~ "windows" ]]; then
|
||||
cp "target/${{ matrix.target }}/release/$BIN_NAME.exe" "dist/"
|
||||
else
|
||||
cp "target/${{ matrix.target }}/release/$BIN_NAME" "dist/"
|
||||
fi
|
||||
|
||||
if [[ "${{ matrix.build }}" =~ "linux" ]]; then
|
||||
cp "target/${{ matrix.target }}/debian/rustscan_${TAG}_amd64.deb" "dist/"
|
||||
fi
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
name: bins-${{ matrix.build }}
|
||||
path: dist
|
||||
|
||||
publish:
|
||||
name: Publish
|
||||
needs: [dist]
|
||||
if: contains(fromJson('["push", "schedule"]'), github.event_name) && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
# with:
|
||||
# path: dist
|
||||
# - run: ls -al ./dist
|
||||
- run: ls -al bins-*
|
||||
|
||||
- name: Calculate tag name
|
||||
run: |
|
||||
name=dev
|
||||
if [[ ${GITHUB_REF} =~ refs/tags/[0-9]+.[0-9]+.[0-9]+ ]]; then
|
||||
name=${GITHUB_REF#refs/tags/}
|
||||
fi
|
||||
echo ::set-output name=val::$name
|
||||
echo TAG=$name >> $GITHUB_ENV
|
||||
id: tagname
|
||||
|
||||
- name: Build archive
|
||||
shell: bash
|
||||
run: |
|
||||
set -ex
|
||||
rm -rf tmp
|
||||
mkdir tmp
|
||||
mkdir dist
|
||||
for dir in bins-* ; do
|
||||
platform=${dir#"bins-"}
|
||||
unset exe
|
||||
if [[ $platform =~ "windows" ]]; then
|
||||
exe=".exe"
|
||||
fi
|
||||
pkgname=$PROJECT_NAME-$TAG-$platform
|
||||
mkdir tmp/$pkgname
|
||||
# cp LICENSE README.md tmp/$pkgname
|
||||
mv bins-$platform/$BIN_NAME$exe tmp/$pkgname
|
||||
chmod +x tmp/$pkgname/$BIN_NAME$exe
|
||||
|
||||
if [[ $platform =~ "linux" ]]; then
|
||||
mv "bins-$platform/rustscan_${TAG}_amd64.deb" dist/
|
||||
fi
|
||||
|
||||
if [ "$exe" = "" ]; then
|
||||
tar cJf dist/$pkgname.tar.xz -C tmp $pkgname
|
||||
else
|
||||
(cd tmp && 7z a -r ../dist/$pkgname.zip $pkgname)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: dist/*
|
||||
file_glob: true
|
||||
tag: ${{ steps.tagname.outputs.val }}
|
||||
overwrite: true
|
||||
|
||||
- name: Extract version
|
||||
id: extract-version
|
||||
run: |
|
||||
printf "::set-output name=%s::%s\n" tag-name "${GITHUB_REF#refs/tags/}"
|
||||
- uses: mislav/bump-homebrew-formula-action@v3
|
||||
with:
|
||||
formula-path: ${{env.PROJECT_NAME}}.rb
|
||||
homebrew-tap: ${{ env.BREW_TAP }}
|
||||
download-url: 'https://github.com/${{ env.REPO_NAME }}/releases/download/${{ steps.extract-version.outputs.tag-name }}/${{env.PROJECT_NAME}}-${{ steps.extract-version.outputs.tag-name }}-x86_64-macos.tar.xz'
|
||||
commit-message: updating formula for ${{ env.PROJECT_NAME }}
|
||||
env:
|
||||
COMMITTER_TOKEN: ${{ secrets.COMMITTER_TOKEN }}
|
||||
#
|
||||
# you can use this initial file in your homebrew-tap if you don't have an initial formula:
|
||||
# <projectname>.rb
|
||||
#
|
||||
# class <Projectname capitalized> < Formula
|
||||
# desc "A test formula"
|
||||
# homepage "http://www.example.com"
|
||||
# url "-----"
|
||||
# version "-----"
|
||||
# sha256 "-----"
|
||||
|
||||
# def install
|
||||
# bin.install "<bin-name>"
|
||||
# end
|
||||
# end
|
||||
|
||||
# Uncomment this section if you want to release your package to crates.io
|
||||
# Before publishing, make sure you have filled out the following fields:
|
||||
# license or license-file, description, homepage, documentation, repository, readme.
|
||||
# Read more: https://doc.rust-lang.org/cargo/reference/publishing.html
|
||||
|
||||
- name: Install ${{ matrix.rust }} toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- run: cargo publish --token ${CRATES_TOKEN}
|
||||
env:
|
||||
CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}
|
||||
@@ -120,3 +120,5 @@ fabric.properties
|
||||
.idea/*
|
||||
.idea
|
||||
.idea*
|
||||
|
||||
settings.json
|
||||
Generated
+795
-616
File diff suppressed because it is too large.
Load diff
+12
-7
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "rustscan"
|
||||
version = "2.0.0"
|
||||
authors = ["Bee <brando[email protected]>"]
|
||||
version = "2.1.1"
|
||||
authors = ["Autumn <autum[email protected]>"]
|
||||
edition = "2018"
|
||||
description = "Faster Nmap Scanning with Rust"
|
||||
homepage = "https://github.com/rustscan/rustscan"
|
||||
@@ -17,12 +17,11 @@ exclude = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
colored = "2.0.0"
|
||||
colored = "2.1.0"
|
||||
structopt = "0.3.20"
|
||||
async-std = "1.7.0"
|
||||
futures = "0.3"
|
||||
rlimit = "0.5.4"
|
||||
shell-words = "1.0.0"
|
||||
rlimit = "0.8.3"
|
||||
log = "0.4.0"
|
||||
env_logger = "0.8.2"
|
||||
dirs = "3.0.1"
|
||||
@@ -35,10 +34,10 @@ serde = "1.0.124"
|
||||
serde_derive = "1.0.116"
|
||||
cidr-utils = "0.5.1"
|
||||
itertools = "0.9.0"
|
||||
trust-dns-resolver = { version = "0.19.5", features = ["dns-over-rustls"] }
|
||||
trust-dns-resolver = { version = "0.23.2", features = ["dns-over-rustls"] }
|
||||
anyhow = "1.0.40"
|
||||
subprocess = "0.2.6"
|
||||
text_placeholder = { version = "0.4", features = ["struct_context"] }
|
||||
text_placeholder = { version = "0.5", features = ["struct_context"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wait-timeout = "0.2"
|
||||
@@ -50,3 +49,9 @@ section = "rust"
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
strip = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[[bin]]
|
||||
name = "rustscan"
|
||||
path = "src/main.rs"
|
||||
+11
-5
@@ -1,19 +1,25 @@
|
||||
# Build Stage
|
||||
FROM rust:alpine as builder
|
||||
LABEL maintainer="RustScan <https://github.com/RustScan>"
|
||||
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
|
||||
WORKDIR /app/rustscan
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY src/ src/
|
||||
RUN cargo install --path .
|
||||
RUN cargo build --release
|
||||
|
||||
FROM alpine:3.12
|
||||
# Release Stage
|
||||
FROM alpine:3.19.1 as release
|
||||
LABEL author="Hydragyrum <https://github.com/Hydragyrum>"
|
||||
LABEL author="LeoFVO <https://github.com/LeoFVO>"
|
||||
|
||||
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
|
||||
apk add --no-cache nmap nmap-scripts wget ca-certificates bind-tools
|
||||
|
||||
USER rustscan
|
||||
COPY --from=builder /app/rustscan/target/release/rustscan /usr/local/bin/rustscan
|
||||
|
||||
ENTRYPOINT [ "/usr/local/bin/rustscan" ]
|
||||
@@ -1,35 +1,29 @@
|
||||
<p align="center">
|
||||
➡️
|
||||
<a href="http://discord.skerritt.blog">Discord</a> |
|
||||
<a href="https://github.com/RustScan/RustScan/wiki/Installation-Guide">Installation Guide</a> |
|
||||
<a href="https://github.com/RustScan/RustScan#-usage">Usage Guide</a>
|
||||
⬅️
|
||||
<br>
|
||||
<div align="center" markdown="1">
|
||||
|
||||
➡️ [Discord][discord] | [Installation Guide][toc-install] | [Usage Guide][usage-guide] ⬅️
|
||||
|
||||
<img src="pictures/rustscan.png" height=400px width=400px>
|
||||
</p>
|
||||
<p align="center">
|
||||
<u><b> The Modern Port Scanner. </b></u><br><b>Fast, smart, effective.</b>
|
||||
</p>
|
||||
<p align="center">
|
||||
<img alt="AUR version" src="https://img.shields.io/aur/version/rustscan">
|
||||
<img src="https://img.shields.io/badge/Built%20with-Rust-Purple">
|
||||
<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/754001738184392704">
|
||||
<img alt="Actions" src="https://github.com/RustScan/RustScan/workflows/Continuous%20integration/badge.svg?branch=master">
|
||||
</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">🔧 Homebrew </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://raw.githubusercontent.com/RustScan/RustScan/master/pictures/apple.png" /></p> |
|
||||
| `docker pull rustscan/rustscan:2.0.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` | `brew install rustscan` |
|
||||
<!--<u>**The Modern Port Scanner.**</u>-->
|
||||
**Fast, smart, effective.**
|
||||
|
||||
<hr>
|
||||
![Arch Linux package][badge-1] ![Built with Rust][badge-2] ![GitHub All Releases][badge-3] ![Crates.io][badge-4] ![Discord][badge-5] ![Actions][badge-6]
|
||||
|
||||
|
||||
---
|
||||
|
||||
| 🐋 `(Recommended)` | 👩💻 | 🏗️ | 🔧 |
|
||||
| :---------------------------------------------------------: | :---------------------------: | :-------------------------: | :-------------------------: |
|
||||
| [![Docker][DockerPic]][Docker] | [![Kali][Kali1]][Kali/Debian] | [![Arch][Arch]][Arch-Linux] | [![apple][apple]][Homebrew] |
|
||||
| `docker pull rustscan/rustscan:2.1.1`</br>[Docker][usage-0] | [Link to Documentation][kali] | `yay -S rustscan` | `brew install rustscan` |
|
||||
|
||||
---
|
||||
|
||||
</div>
|
||||
|
||||
# 🤔 What is this?
|
||||
|
||||

|
||||
![fast][speed-1]
|
||||
|
||||
The Modern Port Scanner. **Find ports quickly (3 seconds at its fastest)**. Run scripts through our scripting engine (Python, Lua, Shell supported).
|
||||
|
||||
@@ -43,17 +37,17 @@ The Modern Port Scanner. **Find ports quickly (3 seconds at its fastest)**. Run
|
||||
|
||||
## ‼️ Important Links
|
||||
|
||||
| Installation Guide | Documentation | Discord |
|
||||
| -------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------- |
|
||||
| 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://rustscan.github.io/RustScan/) | 🦜 [Discord](http://discord.skerritt.blog) |
|
||||
| <!--Installation Guide--> | <!--Documentation--> | <!--Discord--> |
|
||||
| :----------------------------------------: | :------------------------------------: | :-------------------------: |
|
||||
| :book: [Installation Guide][links-table-1] | :books: [Documentation][links-table-2] | :parrot: [Discord][discord] |
|
||||
|
||||
## 🙋 Table of Contents
|
||||
|
||||
- 📖 [Installation Guide](https://github.com/RustScan/RustScan/wiki/Installation-Guide)
|
||||
- 🐋 [Docker Usage](https://github.com/RustScan/RustScan/wiki/Installation-Guide)
|
||||
- 🦜 [Discord](http://discord.skerritt.blog)
|
||||
- 🤸 [Usage](https://github.com/RustScan/RustScan/wiki/Usage)
|
||||
- 🎪 [Community](https://github.com/RustScan/RustScan#-community)
|
||||
- 📖 [Installation Guide][toc-install]
|
||||
- 🐋 [Docker Usage][toc-docker-usage]
|
||||
- 🦜 [Discord][discord]
|
||||
- 🤸 [Usage][usage-1]
|
||||
- 🎪 [Community][toc-community]
|
||||
|
||||
# 🔭 Why RustScan?
|
||||
|
||||
@@ -63,83 +57,85 @@ Not to mention RustScan uses Adaptive Learning to improve itself over time, maki
|
||||
|
||||
## 🧋 Speed
|
||||
|
||||

|
||||
![fast][speed-1]
|
||||
|
||||
Speed is guaranteed via RustScan. However, if you want to run a slow scan due to stealth that is possible too.
|
||||
Speed is guaranteed via RustScan. However, if you want to run a slow scan due to stealth, that is possible too.
|
||||
|
||||
Firstly, let's talk code.
|
||||
|
||||
We have tests that check to see if RustScan is significantly slower than the previous version. If it is, the continuous integration fails and we can't commit code to master unless we make it faster.
|
||||
We have tests that check to see if RustScan is significantly slower than the previous version. If it is, the continuous integration fails, and we can't commit code to master unless we make it faster.
|
||||
|
||||
[HyperFine](https://github.com/sharkdp/hyperfine) is used to monitor RustScan's performance over time to answer the question "Are we getting faster? Are we getting slower?".
|
||||
[HyperFine][speed-2] is used to monitor RustScan's performance over time to answer the question, "Are we getting faster? Are we getting slower?".
|
||||
|
||||
Every pull request is reviewed by 1 person, but more often than not 2 people review it. We test it manually and make sure the code doesn't affect performance negatively.
|
||||
Every pull request is reviewed by **one** person, but more often than not, **two** people review it. We test it manually and ensure the code doesn't negatively affect performance.
|
||||
|
||||
[Read more here](https://github.com/RustScan/RustScan/wiki/Increasing-Speed-&-Accuracy).
|
||||
[Read more here][speed-3].
|
||||
|
||||
## ⚙️ Extensible
|
||||
## ⚙️ Extensible
|
||||
|
||||

|
||||
![scripts][extensible-1]
|
||||
|
||||
_RustScan piping results into the custom Python script_
|
||||
### _RustScan piping results into the custom Python script_
|
||||
|
||||
RustScan has a new scripting engine which allows anyone to write scripts in most languages. Python, Lua, Shell are all supported.
|
||||
RustScan has a new scripting engine that allows anyone to write scripts in most languages. Python, Lua, and Shell are all supported.
|
||||
|
||||
Want to take your found ports and pipe them into Nmap for further analysis? That's possible. Want to run `smb-enum` if SMB is found open? Possible.
|
||||
|
||||
The possibilities are endless -- and you can write scripts in whatever language you feel comfortable with.
|
||||
|
||||
[Read more here](https://github.com/RustScan/RustScan/wiki/RustScan-Scripting-Engine).
|
||||
[Read more here][extensible-2].
|
||||
|
||||
## 🌊 Adaptive
|
||||
|
||||

|
||||
![adaptive][adaptive-1]
|
||||
|
||||
_RustScan automatically fine-tuning itself to match the host OS_.
|
||||
### _RustScan automatically fine-tunes itself to match the host OS_
|
||||
|
||||
RustScan has a cool set of features called "Adaptive Learning". These features "learn" about the environment you are scanning and how _you_ use RustScan to **improve itself over time**.
|
||||
|
||||
This is an umbrella term we use for any feature that fits this criteria. The list is constantly changing, so [check out our wiki for more information](https://github.com/RustScan/RustScan/wiki/Adaptive-Learning).
|
||||
We use this umbrella term for any feature that fits this criterion. The list constantly changes, so [check out our wiki for more information][adaptive-learning].
|
||||
|
||||
## 👩🦯 Accessible
|
||||
|
||||

|
||||
![fast][accessible-1]
|
||||
|
||||
RustScan is one of the first penetration testing tools that aims to be entirely accessible.
|
||||
RustScan is one of the first penetration testing tools that aims to be entirely accessible.
|
||||
|
||||
[Most penetration testing tools are not accessible](https://bees.substack.com/p/making-hacking-accessible), which negatively affects the whole industry.
|
||||
[Most penetration testing tools are not accessible][accessible-2], which negatively affects the whole industry.
|
||||
|
||||
RustScan has continuous integration testing that aims to make sure it is accessible, and we are constantly working on ways to improve our accessibility and make sure _everyone_ can use RustScan.
|
||||
RustScan has continuous integration testing that aims to ensure it is accessible, and we are constantly working on ways to improve our accessibility and ensure _everyone_ can use RustScan.
|
||||
|
||||
# 📖 Full Installation Guide
|
||||
|
||||
You can find our guide [here](https://github.com/RustScan/RustScan/wiki/Installation-Guide).
|
||||
You can find our guide [here][install-1].
|
||||
|
||||
## 🦊 Community Distributions
|
||||
|
||||
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.
|
||||
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/)
|
||||
- [Open Suse][distributions-1]
|
||||
- [Fedora/CentOS][distributions-2]
|
||||
|
||||
[](https://repology.org/project/rustscan/versions)
|
||||
[![Packaging status][rustscan-svg]][repology-1]
|
||||
|
||||
# 🤸 Usage
|
||||
|
||||
We have 2 usage guides. [Basic Usage](https://github.com/RustScan/RustScan/wiki/Usage) and [Things you may want to do](https://github.com/RustScan/RustScan/wiki/Things-you-may-want-to-do-with-RustScan-but-don't-understand-how).
|
||||
We have 2 usage guides. [Basic Usage][usage-1] and [Things you may want to do][usage-2].
|
||||
|
||||
We also have documentation about our config file [here](https://github.com/RustScan/RustScan/wiki/Config-File).
|
||||
We also have documentation about our config file [here][config-file-here].
|
||||
|
||||
# 🎪 Community
|
||||
|
||||
[Read this to learn how to contribute](https://github.com/RustScan/RustScan/wiki/Contributing).
|
||||
[Contributing][community-1] Read this to learn how.
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
|
||||
[](#contributors-)
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
@@ -159,7 +155,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
</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://tgotwig.dev"><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>
|
||||
@@ -186,6 +182,54 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
<!--Links-->
|
||||
|
||||
[Docker]: https://hub.docker.com/r/cmnatic/rustscan "This is the recommended distribution of rustscan"
|
||||
[kali]: https://github.com/RustScan/RustScan/wiki/Installation-Guide#%EF%B8%8F-debian--kali "Read the install guide"
|
||||
[Kali/Debian]: https://github.com/RustScan/RustScan/releases "Kali Debian"
|
||||
[Arch-Linux]: https://archlinux.org/packages/extra/x86_64/rustscan/ "Arch Linux installation of Rustscan"
|
||||
[Homebrew]: https://crates.io/crates/rustscan/ "Homebrew install of Rustscan"
|
||||
[usage-1]: https://github.com/RustScan/RustScan/wiki/Usage "Basic Usage of Rustscan"
|
||||
[usage-0]: https://github.com/RustScan/RustScan/wiki/Installation-Guide#docker-whale "Use Docker Rustscan"
|
||||
[config-file-here]: https://github.com/RustScan/RustScan/wiki/Config-File "RustScan Configuration File"
|
||||
[usage-2]: https://github.com/RustScan/RustScan/wiki/Things-you-may-want-to-do-with-RustScan-but-don't-understand-how "Things you may want to do with rustscan but don't know how"
|
||||
[community-1]: https://github.com/RustScan/RustScan/wiki/Contributing "Learn how to contribute"
|
||||
[distributions-1]: https://software.opensuse.org/package/rustscan?search_term=rustscan "Open Suse rustscan distribution"
|
||||
[distributions-2]: https://copr.fedorainfracloud.org/coprs/atim/rustscan/ "Rustscan in Fedora"
|
||||
[repology-1]: https://repology.org/project/rustscan/versions "Packaging Status"
|
||||
[install-1]: https://github.com/RustScan/RustScan/wiki/Installation-Guide "Installation guide"
|
||||
[accessible-2]: https://bees.substack.com/p/making-hacking-accessible "Making Hacking Accessible"
|
||||
[extensible-2]: https://github.com/RustScan/RustScan/wiki/RustScan-Scripting-Engine "Scripting Engine"
|
||||
[speed-2]: https://github.com/sharkdp/hyperfine "Hyperfine"
|
||||
[speed-3]: https://github.com/RustScan/RustScan/wiki/Increasing-Speed-&-Accuracy "Increasing Speed & Accuracy"
|
||||
[toc-community]: https://github.com/RustScan/RustScan#-community "Community"
|
||||
[links-table-1]: https://github.com/RustScan/RustScan#-full-installation-guide "Full installation guide"
|
||||
[links-table-2]: https://rustscan.github.io/RustScan/ "Rustscan"
|
||||
[discord]: http://discord.skerritt.blog "Discord blog"
|
||||
[toc-install]: https://github.com/RustScan/RustScan/wiki/Installation-Guide "Installation Guide Wiki"
|
||||
[toc-docker-usage]: https://github.com/RustScan/RustScan/wiki/Installation-Guide#docker- "Docker Installation Guide Wiki"
|
||||
[usage-guide]: https://github.com/RustScan/RustScan#-usage
|
||||
[adaptive-learning]: https://github.com/RustScan/RustScan/wiki/Adaptive-Learning "Adaptive Learning"
|
||||
|
||||
<!--Pictures-->
|
||||
|
||||
[DockerPic]: https://github.com/RustScan/RustScan/blob/master/pictures/docker.png?raw=true "Docker install"
|
||||
[Kali1]: https://github.com/RustScan/RustScan/blob/master/pictures/kali.png?raw=true "Kali Picture"
|
||||
[Arch]: https://github.com/RustScan/RustScan/blob/master/pictures/arch.png?raw=true "Arch Linux"
|
||||
[Apple]: https://raw.githubusercontent.com/RustScan/RustScan/master/pictures/apple.png?size "Apple"
|
||||
[rustscan-svg]: https://repology.org/badge/vertical-allrepos/rustscan.svg "Picture of rustscan repology"
|
||||
[accessible-1]: pictures/accessible.gif "Fast"
|
||||
[adaptive-1]: pictures/adaptive.gif "Adaptive"
|
||||
[extensible-1]: pictures/scripts.gif "Scripts"
|
||||
[speed-1]: pictures/fast.gif "Speed"
|
||||
[badge-1]: https://img.shields.io/archlinux/v/extra/x86_64/rustscan?style=plastic&logo=archlinux&link=https%3A%2F%2Farchlinux.org%2Fpackages%2Fextra%2Fx86_64%2Frustscan%2F
|
||||
[badge-2]: https://img.shields.io/badge/Built%20with-Rust-Purple
|
||||
[badge-3]: https://img.shields.io/github/downloads/rustscan/rustscan/total?label=GitHub%20Downloads
|
||||
[badge-4]: https://img.shields.io/crates/d/rustscan?label=Cargo%20Downloads
|
||||
[badge-5]: https://img.shields.io/discord/754001738184392704
|
||||
[badge-6]: https://github.com/RustScan/RustScan/workflows/Continuous%20integration/badge.svg?branch=master
|
||||
+15
-12
@@ -7,19 +7,21 @@ 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** with no chance to get unbanned. No warnings either. 🤗
|
||||
|
||||
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 with help!
|
||||
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22)
|
||||
|
||||
- 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 with help!
|
||||
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22)
|
||||
|
||||
If you want to, solve the issue or comment on the issue for help.
|
||||
|
||||
The [flow for contributing](https://guides.github.com/introduction/flow/) to open source software is:
|
||||
* Fork the repo
|
||||
* Make changes
|
||||
* Pull request to the repo
|
||||
The flow for contributing to open source software is:
|
||||
|
||||
- Fork the repo
|
||||
- Make changes
|
||||
- Pull request to the repo
|
||||
|
||||
And then comment on the issue that you've done.
|
||||
|
||||
@@ -28,6 +30,7 @@ RustScan also has some `// TODO`'s in the codebase, which are meant more for the
|
||||
If you have any feature suggestions or bugs, leave a GitHub issue. We welcome any and all support :D
|
||||
|
||||
## Rewarding you
|
||||
|
||||
I cannot pay you :-( But, I can place your GitHub profile on the README under `#Contributors` as a thank you! :)
|
||||
|
||||
## Contributing development environment
|
||||
@@ -39,7 +42,7 @@ To build it you just need to run:
|
||||
you@home:~/RustScan$ docker build -t rustscan_contributing -f contributing.Dockerfile
|
||||
```
|
||||
|
||||
Then you need to run the container with a volume so it can access, *with read and write permissions*, to RustScan files:
|
||||
Then you need to run the container with a volume so it can access, _with read and write permissions_, to RustScan files:
|
||||
|
||||
```bash
|
||||
you@home:~/RustScan$ docker run -ti --rm -v "$PWD":/rustscan -w /rustscan rustscan_contributing bash
|
||||
@@ -63,4 +66,4 @@ You can also format, lint with `clippy` and test the code with the following com
|
||||
root@container:/rustscan# cargo fmt
|
||||
root@container:/rustscan# cargo clippy
|
||||
root@container:/rustscan# cargo test
|
||||
```
|
||||
```
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
cargo publish && cargo build --release && git add Cargo.lock && git commit -m 'Updating lock file' && gpsup
|
||||
@@ -1,20 +0,0 @@
|
||||
FROM rust:latest
|
||||
|
||||
RUN git clone https://github.com/brandonskerritt/RustScan
|
||||
WORKDIR "/RustScan"
|
||||
RUN git pull --force
|
||||
RUN cargo install cargo-deb
|
||||
|
||||
RUN apt update -y && apt upgrade -y
|
||||
RUN apt install libc6-dev-i386 -y
|
||||
RUN git clone --depth=1 https://github.com/raspberrypi/tools /raspberrypi-tools
|
||||
ENV PATH=/raspberrypi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/:$PATH
|
||||
ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
|
||||
RUN mkdir /root/.cargo
|
||||
RUN echo "[target.arm-unknown-linux-gnueabihf]" >> /root/.cargo/config
|
||||
RUN echo "strip = { path = \"arm-linux-gnueabihf-strip\" }" >> /root/.cargo/config
|
||||
RUN echo "objcopy = { path = \"arm-linux-gnueabihf-objcopy\" }" >> /root/.cargo/config
|
||||
|
||||
COPY ./entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd /RustScan
|
||||
git pull --force
|
||||
|
||||
#amd64
|
||||
cargo deb
|
||||
|
||||
#arm64
|
||||
rustup target add arm-unknown-linux-gnueabihf
|
||||
cargo deb --target=arm-unknown-linux-gnueabihf
|
||||
|
||||
#i386
|
||||
rustup target add i686-unknown-linux-gnu
|
||||
cargo deb --target=i686-unknown-linux-gnu
|
||||
|
||||
find target/ -name \*.deb -exec cp {} /debs \;
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
docker build -t rustscan-builder . || exit
|
||||
|
||||
# This creates a volume which binds your currentdirectory/debs to
|
||||
# the location where the deb files get spat out in the container.
|
||||
# You don't need to worry about it. Just chmod +x run.sh && ./run.sh and
|
||||
# you'll get yer .deb file in a few minutes. It runs faster after you've used it the first time.
|
||||
docker run -v "$(pwd)/debs:/debs" rustscan-builder
|
||||
+10
-5
@@ -1,15 +1,20 @@
|
||||
//! Provides functionality to capture timing information for scans.
|
||||
//!
|
||||
//! # Usage
|
||||
//! ```
|
||||
//!
|
||||
//! ```rust
|
||||
//! // Initiate Benchmark vector
|
||||
//! # use rustscan::benchmark::{Benchmark, NamedTimer};
|
||||
//! # use log::info;
|
||||
//! let mut bm = Benchmark::init();
|
||||
//! // Start named timer with name
|
||||
//! let mut example_bench = NamedTimer::start("Example Bench");
|
||||
//! // Stop named timer
|
||||
//! example_bench.stop();
|
||||
//! example_bench.end();
|
||||
//! // Add named timer to Benchmarks
|
||||
//! bm.push(example_bench);
|
||||
//! // Print Benchmark Summary
|
||||
//! info!({}, bm.summary());
|
||||
//! info!("{}", bm.summary());
|
||||
//! ```
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -43,7 +48,7 @@ impl Benchmark {
|
||||
.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.push_str(&format!("\n{0: <10} | {1: <10}s", timer.name, runtime_secs));
|
||||
}
|
||||
}
|
||||
summary
|
||||
@@ -85,5 +90,5 @@ fn benchmark() {
|
||||
assert!(benchmarks
|
||||
.summary()
|
||||
.contains("\nRustScan Benchmark Summary\ntest | 0."));
|
||||
assert_ne!(benchmarks.summary().contains("only_start"), true);
|
||||
assert!(!benchmarks.summary().contains("only_start"));
|
||||
}
|
||||
+69
-49
@@ -1,6 +1,8 @@
|
||||
//! Provides a means to read, parse and hold configuration options for scans.
|
||||
use serde_derive::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use structopt::{clap::arg_enum, StructOpt};
|
||||
|
||||
const LOWEST_PORT_NUMBER: u16 = 1;
|
||||
@@ -10,7 +12,7 @@ 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, Copy, PartialEq)]
|
||||
#[derive(Deserialize, Debug, StructOpt, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ScanOrder {
|
||||
Serial,
|
||||
Random,
|
||||
@@ -22,7 +24,7 @@ arg_enum! {
|
||||
/// - none will avoid running any script, only portscan results will be shown.
|
||||
/// - default will run the default embedded nmap script, that's part of RustScan since the beginning.
|
||||
/// - custom will read the ScriptConfig file and the available scripts in the predefined folders
|
||||
#[derive(Deserialize, Debug, StructOpt, Clone, PartialEq, Copy)]
|
||||
#[derive(Deserialize, Debug, StructOpt, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum ScriptsRequired {
|
||||
None,
|
||||
Default,
|
||||
@@ -31,7 +33,7 @@ arg_enum! {
|
||||
}
|
||||
|
||||
/// Represents the range of ports to be scanned.
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PortRange {
|
||||
pub start: u16,
|
||||
pub end: u16,
|
||||
@@ -67,14 +69,14 @@ fn parse_range(input: &str) -> Result<PortRange, String> {
|
||||
/// 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
|
||||
/// - Discord <http://discord.skerritt.blog>
|
||||
/// - GitHub <https://github.com/RustScan/RustScan>
|
||||
pub struct Opts {
|
||||
/// A list of comma separated CIDRs, IPs, or hosts to be scanned.
|
||||
/// A comma-delimited list or newline-delimited file of separated CIDRs, IPs, or hosts to be scanned.
|
||||
#[structopt(short, long, use_delimiter = true)]
|
||||
pub addresses: Vec<String>,
|
||||
|
||||
/// A list of comma separed ports to be scanned. Example: 80,443,8080.
|
||||
/// A list of comma separated ports to be scanned. Example: 80,443,8080.
|
||||
#[structopt(short, long, use_delimiter = true)]
|
||||
pub ports: Option<Vec<u16>>,
|
||||
|
||||
@@ -86,6 +88,10 @@ pub struct Opts {
|
||||
#[structopt(short, long)]
|
||||
pub no_config: bool,
|
||||
|
||||
/// Custom path to config file
|
||||
#[structopt(short, long, parse(from_os_str))]
|
||||
pub config_path: Option<PathBuf>,
|
||||
|
||||
/// Greppable mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
|
||||
#[structopt(short, long)]
|
||||
pub greppable: bool,
|
||||
@@ -112,7 +118,7 @@ pub struct Opts {
|
||||
|
||||
/// Automatically ups the ULIMIT with the value you provided.
|
||||
#[structopt(short, long)]
|
||||
pub ulimit: Option<rlimit::RawRlim>,
|
||||
pub ulimit: Option<u64>,
|
||||
|
||||
/// The order of scanning to be performed. The "serial" option will
|
||||
/// scan ports in ascending order while the "random" option will scan
|
||||
@@ -130,11 +136,15 @@ pub struct Opts {
|
||||
|
||||
/// The Script arguments to run.
|
||||
/// To use the argument -A, end RustScan's args with '-- -A'.
|
||||
/// Example: 'rustscan -T 1500 -a 127.0.0.1 -- -A -sC'.
|
||||
/// Example: 'rustscan -t 1500 -a 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)'\"")
|
||||
/// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"
|
||||
#[structopt(last = true)]
|
||||
pub command: Vec<String>,
|
||||
|
||||
/// A list of comma separated ports to be excluded from scanning. Example: 80,443,8080.
|
||||
#[structopt(short, long, use_delimiter = true)]
|
||||
pub exclude_ports: Option<Vec<u16>>,
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
@@ -156,8 +166,8 @@ impl Opts {
|
||||
/// 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);
|
||||
self.merge_required(config);
|
||||
self.merge_optional(config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,12 +203,35 @@ impl Opts {
|
||||
if self.top && 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())
|
||||
ports.push(entry.parse().unwrap());
|
||||
}
|
||||
self.ports = Some(ports);
|
||||
}
|
||||
|
||||
merge_optional!(range, ulimit);
|
||||
merge_optional!(range, ulimit, exclude_ports);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Opts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
addresses: vec![],
|
||||
ports: None,
|
||||
range: None,
|
||||
greppable: true,
|
||||
batch_size: 0,
|
||||
timeout: 0,
|
||||
tries: 0,
|
||||
ulimit: None,
|
||||
command: vec![],
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: true,
|
||||
top: false,
|
||||
scripts: ScriptsRequired::Default,
|
||||
config_path: None,
|
||||
exclude_ports: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,13 +249,15 @@ pub struct Config {
|
||||
batch_size: Option<u16>,
|
||||
timeout: Option<u32>,
|
||||
tries: Option<u8>,
|
||||
ulimit: Option<rlimit::RawRlim>,
|
||||
ulimit: Option<u64>,
|
||||
scan_order: Option<ScanOrder>,
|
||||
command: Option<Vec<String>>,
|
||||
scripts: Option<ScriptsRequired>,
|
||||
exclude_ports: Option<Vec<u16>>,
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
#[allow(clippy::doc_link_with_quotes)]
|
||||
impl Config {
|
||||
/// Reads the configuration file with TOML format and parses it into a
|
||||
/// Config struct.
|
||||
@@ -233,17 +268,13 @@ impl Config {
|
||||
/// ports = [80, 443, 8080]
|
||||
/// greppable = true
|
||||
/// scan_order: "Serial"
|
||||
/// exclude_ports = [8080, 9090, 80]
|
||||
///
|
||||
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");
|
||||
|
||||
pub fn read(custom_config_path: Option<PathBuf>) -> Self {
|
||||
let mut content = String::new();
|
||||
if home_dir.exists() {
|
||||
content = match fs::read_to_string(home_dir) {
|
||||
let config_path = custom_config_path.unwrap_or_else(default_config_path);
|
||||
if config_path.exists() {
|
||||
content = match fs::read_to_string(config_path) {
|
||||
Ok(content) => content,
|
||||
Err(_) => String::new(),
|
||||
}
|
||||
@@ -252,7 +283,7 @@ impl Config {
|
||||
let config: Config = match toml::from_str(&content) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
println!("Found {} in configuration file.\nAborting scan.\n", e);
|
||||
println!("Found {e} in configuration file.\nAborting scan.\n");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
@@ -261,6 +292,15 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs default path to config toml
|
||||
pub fn default_config_path() -> PathBuf {
|
||||
let Some(mut config_path) = dirs::home_dir() else {
|
||||
panic!("Could not infer config file path.");
|
||||
};
|
||||
config_path.push(".rustscan.toml");
|
||||
config_path
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Config, Opts, PortRange, ScanOrder, ScriptsRequired};
|
||||
@@ -279,27 +319,7 @@ mod tests {
|
||||
accessible: Some(true),
|
||||
scan_order: Some(ScanOrder::Random),
|
||||
scripts: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Opts {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
addresses: vec![],
|
||||
ports: None,
|
||||
range: None,
|
||||
greppable: true,
|
||||
batch_size: 0,
|
||||
timeout: 0,
|
||||
tries: 0,
|
||||
ulimit: None,
|
||||
command: vec![],
|
||||
accessible: false,
|
||||
scan_order: ScanOrder::Serial,
|
||||
no_config: true,
|
||||
top: false,
|
||||
scripts: ScriptsRequired::Default,
|
||||
exclude_ports: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,8 +332,8 @@ mod tests {
|
||||
opts.merge(&config);
|
||||
|
||||
assert_eq!(opts.addresses, vec![] as Vec<String>);
|
||||
assert_eq!(opts.greppable, true);
|
||||
assert_eq!(opts.accessible, false);
|
||||
assert!(opts.greppable);
|
||||
assert!(!opts.accessible);
|
||||
assert_eq!(opts.timeout, 0);
|
||||
assert_eq!(opts.command, vec![] as Vec<String>);
|
||||
assert_eq!(opts.scan_order, ScanOrder::Serial);
|
||||
@@ -332,7 +352,7 @@ mod tests {
|
||||
assert_eq!(opts.command, config.command.unwrap());
|
||||
assert_eq!(opts.accessible, config.accessible.unwrap());
|
||||
assert_eq!(opts.scan_order, config.scan_order.unwrap());
|
||||
assert_eq!(opts.scripts, ScriptsRequired::Default)
|
||||
assert_eq!(opts.scripts, ScriptsRequired::Default);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
//! This crate exposes the internal functionality of the
|
||||
//! [RustScan](https://rustscan.github.io/RustScan) port scanner.
|
||||
//!
|
||||
//! ## Example: perform a scan against localhost
|
||||
//!
|
||||
//! The core scanning behaviour is managed by
|
||||
//! [`Scanner`](crate::scanner::Scanner) which in turn requires a
|
||||
//! [`PortStrategy`](crate::port_strategy::PortStrategy):
|
||||
//!
|
||||
//! ```rust
|
||||
//! use async_std::task::block_on;
|
||||
//! use std::{net::IpAddr, time::Duration};
|
||||
//!
|
||||
//! use rustscan::input::{PortRange, ScanOrder};
|
||||
//! use rustscan::port_strategy::PortStrategy;
|
||||
//! use rustscan::scanner::Scanner;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! 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),
|
||||
//! 1,
|
||||
//! true,
|
||||
//! strategy,
|
||||
//! true,
|
||||
//! vec![9000],
|
||||
//! );
|
||||
//!
|
||||
//! let scan_result = block_on(scanner.run());
|
||||
//!
|
||||
//! println!("{:?}", scan_result);
|
||||
//! }
|
||||
//! ```
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
|
||||
pub mod tui;
|
||||
|
||||
pub mod input;
|
||||
|
||||
pub mod scanner;
|
||||
|
||||
pub mod port_strategy;
|
||||
|
||||
pub mod benchmark;
|
||||
|
||||
pub mod scripts;
|
||||
+76
-68
@@ -2,31 +2,17 @@
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::doc_markdown, clippy::if_not_else, clippy::non_ascii_literal)]
|
||||
|
||||
extern crate shell_words;
|
||||
|
||||
mod tui;
|
||||
|
||||
mod input;
|
||||
use input::{Config, Opts, PortRange, ScanOrder, ScriptsRequired};
|
||||
|
||||
mod scanner;
|
||||
use scanner::Scanner;
|
||||
|
||||
mod port_strategy;
|
||||
use port_strategy::PortStrategy;
|
||||
|
||||
mod benchmark;
|
||||
use benchmark::{Benchmark, NamedTimer};
|
||||
|
||||
mod scripts;
|
||||
use scripts::{init_scripts, Script, ScriptFile};
|
||||
use rustscan::benchmark::{Benchmark, NamedTimer};
|
||||
use rustscan::input::{self, Config, Opts, ScriptsRequired};
|
||||
use rustscan::port_strategy::PortStrategy;
|
||||
use rustscan::scanner::Scanner;
|
||||
use rustscan::scripts::{init_scripts, Script, ScriptFile};
|
||||
use rustscan::{detail, funny_opening, output, warning};
|
||||
|
||||
use cidr_utils::cidr::IpCidr;
|
||||
use colorful::{Color, Colorful};
|
||||
use futures::executor::block_on;
|
||||
use rlimit::{getrlimit, setrlimit, RawRlim, Resource, Rlim};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{prelude::*, BufReader};
|
||||
use std::net::{IpAddr, ToSocketAddrs};
|
||||
@@ -42,9 +28,10 @@ extern crate colorful;
|
||||
extern crate dirs;
|
||||
|
||||
// Average value for Ubuntu
|
||||
const DEFAULT_FILE_DESCRIPTORS_LIMIT: RawRlim = 8000;
|
||||
#[cfg(unix)]
|
||||
const DEFAULT_FILE_DESCRIPTORS_LIMIT: u64 = 8000;
|
||||
// Safest batch size based on experimentation
|
||||
const AVERAGE_BATCH_SIZE: RawRlim = 3000;
|
||||
const AVERAGE_BATCH_SIZE: u16 = 3000;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
@@ -59,7 +46,7 @@ fn main() {
|
||||
let mut rustscan_bench = NamedTimer::start("RustScan");
|
||||
|
||||
let mut opts: Opts = Opts::read();
|
||||
let config = Config::read();
|
||||
let config = Config::read(opts.config_path.clone());
|
||||
opts.merge(&config);
|
||||
|
||||
debug!("Main() `opts` arguments are {:?}", opts);
|
||||
@@ -68,7 +55,7 @@ fn main() {
|
||||
Ok(scripts_to_run) => scripts_to_run,
|
||||
Err(e) => {
|
||||
warning!(
|
||||
format!("Initiating scripts failed!\n{}", e.to_string()),
|
||||
format!("Initiating scripts failed!\n{e}"),
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
@@ -93,9 +80,15 @@ fn main() {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let ulimit: RawRlim = adjust_ulimit_size(&opts);
|
||||
let batch_size: u16 = infer_batch_size(&opts, ulimit);
|
||||
#[cfg(unix)]
|
||||
let batch_size: u16 = infer_batch_size(&opts, adjust_ulimit_size(&opts));
|
||||
|
||||
#[cfg(not(unix))]
|
||||
let batch_size: u16 = AVERAGE_BATCH_SIZE;
|
||||
|
||||
// Added by wasuaje - 01/26/2024:
|
||||
// exclude_ports is an exclusion port list
|
||||
//
|
||||
let scanner = Scanner::new(
|
||||
&ips,
|
||||
batch_size,
|
||||
@@ -104,6 +97,7 @@ fn main() {
|
||||
opts.greppable,
|
||||
PortStrategy::pick(&opts.range, opts.ports, opts.scan_order),
|
||||
opts.accessible,
|
||||
opts.exclude_ports.unwrap_or_default(),
|
||||
);
|
||||
debug!("Scanner finished building: {:?}", scanner);
|
||||
|
||||
@@ -176,7 +170,7 @@ fn main() {
|
||||
let script = Script::build(
|
||||
script_f.path,
|
||||
*ip,
|
||||
ports.to_vec(),
|
||||
ports.clone(),
|
||||
script_f.port,
|
||||
script_f.ports_separator,
|
||||
script_f.tags,
|
||||
@@ -187,11 +181,7 @@ fn main() {
|
||||
detail!(script_result.to_string(), opts.greppable, opts.accessible);
|
||||
}
|
||||
Err(e) => {
|
||||
warning!(
|
||||
&format!("Error {}", e.to_string()),
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
warning!(&format!("Error {e}"), opts.greppable, opts.accessible);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,27 +197,35 @@ fn main() {
|
||||
}
|
||||
|
||||
/// Prints the opening title of RustScan
|
||||
#[allow(clippy::items_after_statements, clippy::needless_raw_string_hashes)]
|
||||
fn print_opening(opts: &Opts) {
|
||||
debug!("Printing opening");
|
||||
let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-.
|
||||
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
|
||||
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
|
||||
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
|
||||
The Modern Day Port Scanner."#;
|
||||
let s = format!(
|
||||
"{}\n{}\n{}\n{}\n{}",
|
||||
r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-."#,
|
||||
r#"| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |"#,
|
||||
r#"| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |"#,
|
||||
r#"`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'"#,
|
||||
r#"The Modern Day Port Scanner."#
|
||||
);
|
||||
println!("{}", s.gradient(Color::Green).bold());
|
||||
let info = r#"________________________________________
|
||||
: https://discord.gg/GFrQsGy :
|
||||
: https://github.com/RustScan/RustScan :
|
||||
--------------------------------------"#;
|
||||
let info = format!(
|
||||
"{}\n{}\n{}\n{}",
|
||||
r#"________________________________________"#,
|
||||
r#": http://discord.skerritt.blog :"#,
|
||||
r#": https://github.com/RustScan/RustScan :"#,
|
||||
r#" --------------------------------------"#
|
||||
);
|
||||
println!("{}", info.gradient(Color::Yellow).bold());
|
||||
funny_opening!();
|
||||
|
||||
let config_path = dirs::home_dir()
|
||||
.expect("Could not infer config file path.")
|
||||
.join(".rustscan.toml");
|
||||
let config_path = opts
|
||||
.config_path
|
||||
.clone()
|
||||
.unwrap_or_else(input::default_config_path);
|
||||
|
||||
detail!(
|
||||
format!("The config file is expected to be at {:?}", config_path),
|
||||
format!("The config file is expected to be at {config_path:?}"),
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
@@ -256,7 +254,7 @@ fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
|
||||
|
||||
if !file_path.is_file() {
|
||||
warning!(
|
||||
format!("Host {:?} could not be resolved.", file_path),
|
||||
format!("Host {file_path:?} could not be resolved."),
|
||||
input.greppable,
|
||||
input.accessible
|
||||
);
|
||||
@@ -268,7 +266,7 @@ fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
|
||||
ips.extend(x);
|
||||
} else {
|
||||
warning!(
|
||||
format!("Host {:?} could not be resolved.", file_path),
|
||||
format!("Host {file_path:?} could not be resolved."),
|
||||
input.greppable,
|
||||
input.accessible
|
||||
);
|
||||
@@ -278,11 +276,11 @@ fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
|
||||
ips
|
||||
}
|
||||
|
||||
/// Given a string, parse it as an host, IP address, or CIDR.
|
||||
/// Given a string, parse it as a 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
|
||||
/// Call this every time you have a possible IP_or_host
|
||||
fn parse_address(address: &str, resolver: &Resolver) -> Vec<IpAddr> {
|
||||
IpCidr::from_str(&address)
|
||||
IpCidr::from_str(address)
|
||||
.map(|cidr| cidr.iter().collect())
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
@@ -294,7 +292,7 @@ fn parse_address(address: &str, resolver: &Resolver) -> Vec<IpAddr> {
|
||||
.unwrap_or_else(|| resolve_ips_from_host(address, resolver))
|
||||
}
|
||||
|
||||
/// Uses DNS to get the IPS assiocated with host
|
||||
/// Uses DNS to get the IPS associated with host
|
||||
fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec<IpAddr> {
|
||||
let mut ips: Vec<std::net::IpAddr> = Vec::new();
|
||||
|
||||
@@ -302,7 +300,7 @@ fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec<IpAddr
|
||||
for ip in addrs {
|
||||
ips.push(ip.ip());
|
||||
}
|
||||
} else if let Ok(addrs) = backup_resolver.lookup_ip(&source) {
|
||||
} else if let Ok(addrs) = backup_resolver.lookup_ip(source) {
|
||||
ips.extend(addrs.iter());
|
||||
}
|
||||
|
||||
@@ -331,13 +329,14 @@ fn read_ips_from_file(
|
||||
Ok(ips)
|
||||
}
|
||||
|
||||
fn adjust_ulimit_size(opts: &Opts) -> RawRlim {
|
||||
if opts.ulimit.is_some() {
|
||||
let limit: Rlim = Rlim::from_raw(opts.ulimit.unwrap());
|
||||
#[cfg(unix)]
|
||||
fn adjust_ulimit_size(opts: &Opts) -> u64 {
|
||||
use rlimit::Resource;
|
||||
|
||||
if setrlimit(Resource::NOFILE, limit, limit).is_ok() {
|
||||
if let Some(limit) = opts.ulimit {
|
||||
if Resource::NOFILE.set(limit, limit).is_ok() {
|
||||
detail!(
|
||||
format!("Automatically increasing ulimit value to {}.", limit),
|
||||
format!("Automatically increasing ulimit value to {limit}."),
|
||||
opts.greppable,
|
||||
opts.accessible
|
||||
);
|
||||
@@ -350,13 +349,15 @@ fn adjust_ulimit_size(opts: &Opts) -> RawRlim {
|
||||
}
|
||||
}
|
||||
|
||||
let (rlim, _) = getrlimit(Resource::NOFILE).unwrap();
|
||||
|
||||
rlim.as_raw()
|
||||
let (soft, _) = Resource::NOFILE.get().unwrap();
|
||||
soft
|
||||
}
|
||||
|
||||
fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 {
|
||||
let mut batch_size: RawRlim = opts.batch_size.into();
|
||||
#[cfg(unix)]
|
||||
fn infer_batch_size(opts: &Opts, ulimit: u64) -> u16 {
|
||||
use std::convert::TryInto;
|
||||
|
||||
let mut batch_size: u64 = opts.batch_size.into();
|
||||
|
||||
// Adjust the batch size when the ulimit value is lower than the desired batch size
|
||||
if ulimit < batch_size {
|
||||
@@ -367,18 +368,18 @@ fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 {
|
||||
// When the OS supports high file limits like 8000, but the user
|
||||
// selected a batch size higher than this we should reduce it to
|
||||
// a lower number.
|
||||
if ulimit < AVERAGE_BATCH_SIZE {
|
||||
if ulimit < AVERAGE_BATCH_SIZE.into() {
|
||||
// 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");
|
||||
batch_size = ulimit / 2
|
||||
batch_size = ulimit / 2;
|
||||
} else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT {
|
||||
info!("Batch size is now average batch size");
|
||||
batch_size = AVERAGE_BATCH_SIZE
|
||||
batch_size = AVERAGE_BATCH_SIZE.into();
|
||||
} else {
|
||||
batch_size = ulimit - 100
|
||||
batch_size = ulimit - 100;
|
||||
}
|
||||
}
|
||||
// When the ulimit is higher than the batch size let the user know that the
|
||||
@@ -395,10 +396,13 @@ fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts};
|
||||
#[cfg(unix)]
|
||||
use super::{adjust_ulimit_size, infer_batch_size};
|
||||
use super::{parse_addresses, print_opening, Opts};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn batch_size_lowered() {
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 50_000;
|
||||
@@ -408,6 +412,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn batch_size_lowered_average_size() {
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 50_000;
|
||||
@@ -416,6 +421,7 @@ mod tests {
|
||||
assert!(batch_size == 3_000);
|
||||
}
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn batch_size_equals_ulimit_lowered() {
|
||||
// because ulimit and batch size are same size, batch size is lowered
|
||||
// to ULIMIT - 100
|
||||
@@ -426,6 +432,7 @@ mod tests {
|
||||
assert!(batch_size == 4_900);
|
||||
}
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn batch_size_adjusted_2000() {
|
||||
// ulimit == batch_size
|
||||
let mut opts = Opts::default();
|
||||
@@ -444,6 +451,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_high_ulimit_no_greppable_mode() {
|
||||
let mut opts = Opts::default();
|
||||
opts.batch_size = 10;
|
||||
@@ -496,7 +504,7 @@ mod tests {
|
||||
opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()];
|
||||
let ips = parse_addresses(&opts);
|
||||
|
||||
assert_eq!(ips.is_empty(), true);
|
||||
assert!(ips.is_empty());
|
||||
}
|
||||
#[test]
|
||||
fn parse_hosts_file_and_incorrect_hosts() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Provides a means to hold configuration options specifically for port scanning.
|
||||
mod range_iterator;
|
||||
use super::{PortRange, ScanOrder};
|
||||
use crate::input::{PortRange, ScanOrder};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use range_iterator::RangeIterator;
|
||||
@@ -44,7 +45,7 @@ impl PortStrategy {
|
||||
|
||||
pub fn order(&self) -> Vec<u16> {
|
||||
match self {
|
||||
PortStrategy::Manual(ports) => ports.to_vec(),
|
||||
PortStrategy::Manual(ports) => ports.clone(),
|
||||
PortStrategy::Serial(range) => range.generate(),
|
||||
PortStrategy::Random(range) => range.generate(),
|
||||
}
|
||||
@@ -67,7 +68,7 @@ pub struct SerialRange {
|
||||
|
||||
impl RangeOrder for SerialRange {
|
||||
fn generate(&self) -> Vec<u16> {
|
||||
(self.start..self.end).collect()
|
||||
(self.start..=self.end).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,14 +98,14 @@ impl RangeOrder for RandomRange {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PortStrategy;
|
||||
use crate::{PortRange, ScanOrder};
|
||||
use crate::input::{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>>();
|
||||
let expected_range = (1..=100).into_iter().collect::<Vec<u16>>();
|
||||
assert_eq!(expected_range, result);
|
||||
}
|
||||
#[test]
|
||||
@@ -112,10 +113,10 @@ mod tests {
|
||||
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>>();
|
||||
let expected_range = (1..=100).into_iter().collect::<Vec<u16>>();
|
||||
assert_ne!(expected_range, result);
|
||||
|
||||
result.sort();
|
||||
result.sort_unstable();
|
||||
assert_eq!(expected_range, result);
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@ mod tests {
|
||||
let expected_range = (1..10).into_iter().collect::<Vec<u16>>();
|
||||
assert_ne!(expected_range, result);
|
||||
|
||||
result.sort();
|
||||
result.sort_unstable();
|
||||
assert_eq!(expected_range, result);
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,16 @@ pub struct RangeIterator {
|
||||
|
||||
/// An iterator that follows the `Linear Congruential Generator` algorithm.
|
||||
///
|
||||
/// For more information: https://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||
/// For more information: <https://en.wikipedia.org/wiki/Linear_congruential_generator>
|
||||
impl RangeIterator {
|
||||
/// Receives the the start and end of a range and normalize
|
||||
/// Receives 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 normalized_end = end - start + 1;
|
||||
let step = pick_random_coprime(normalized_end);
|
||||
|
||||
// Randomly choose a number within the range to be the first
|
||||
@@ -89,9 +89,8 @@ fn pick_random_coprime(end: u32) -> u32 {
|
||||
for _ in 0..10 {
|
||||
if end.gcd(candidate) == 1 {
|
||||
return candidate;
|
||||
} else {
|
||||
candidate = rng.gen_range(lower_range, upper_range);
|
||||
}
|
||||
candidate = rng.gen_range(lower_range, upper_range);
|
||||
}
|
||||
|
||||
end - 1
|
||||
@@ -104,30 +103,30 @@ mod tests {
|
||||
#[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>>();
|
||||
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>>();
|
||||
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>>();
|
||||
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>>();
|
||||
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>>();
|
||||
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.sort_unstable();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
+44
-19
@@ -1,4 +1,6 @@
|
||||
use super::PortStrategy;
|
||||
//! Core functionality for actual scanning behaviour.
|
||||
use crate::port_strategy::PortStrategy;
|
||||
use log::debug;
|
||||
|
||||
mod socket_iterator;
|
||||
use socket_iterator::SocketIterator;
|
||||
@@ -21,6 +23,8 @@ use std::{
|
||||
/// 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.
|
||||
/// greppable is whether or not RustScan should print things, or wait until the end to print only the ip and open ports.
|
||||
/// Added by wasuaje - 01/26/2024:
|
||||
/// exclude_ports is an exclusion port list
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
#[derive(Debug)]
|
||||
pub struct Scanner {
|
||||
@@ -31,8 +35,11 @@ pub struct Scanner {
|
||||
greppable: bool,
|
||||
port_strategy: PortStrategy,
|
||||
accessible: bool,
|
||||
exclude_ports: Vec<u16>,
|
||||
}
|
||||
|
||||
// Allowing too many arguments for clippy.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
impl Scanner {
|
||||
pub fn new(
|
||||
ips: &[IpAddr],
|
||||
@@ -42,6 +49,7 @@ impl Scanner {
|
||||
greppable: bool,
|
||||
port_strategy: PortStrategy,
|
||||
accessible: bool,
|
||||
exclude_ports: Vec<u16>,
|
||||
) -> Self {
|
||||
Self {
|
||||
batch_size,
|
||||
@@ -51,18 +59,27 @@ impl Scanner {
|
||||
port_strategy,
|
||||
ips: ips.iter().map(ToOwned::to_owned).collect(),
|
||||
accessible,
|
||||
exclude_ports,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>
|
||||
/// Returns all open ports as `Vec<u16>`
|
||||
/// Added by wasuaje - 01/26/2024:
|
||||
/// Filtering port against exclude port list
|
||||
pub async fn run(&self) -> Vec<SocketAddr> {
|
||||
let ports: Vec<u16> = self.port_strategy.order();
|
||||
let ports: Vec<u16> = self
|
||||
.port_strategy
|
||||
.order()
|
||||
.iter()
|
||||
.filter(|&port| !self.exclude_ports.contains(port))
|
||||
.copied()
|
||||
.collect();
|
||||
let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports);
|
||||
let mut open_sockets: Vec<SocketAddr> = Vec::new();
|
||||
let mut ftrs = FuturesUnordered::new();
|
||||
let mut errors: HashSet<String> = HashSet::with_capacity(self.ips.len() * 1000);
|
||||
let mut errors: HashSet<String> = HashSet::new();
|
||||
|
||||
for _ in 0..self.batch_size {
|
||||
if let Some(socket) = socket_iterator.next() {
|
||||
@@ -72,7 +89,7 @@ impl Scanner {
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ",
|
||||
debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ",
|
||||
self.batch_size,
|
||||
self.ips.len(),
|
||||
&ports.len(),
|
||||
@@ -100,14 +117,16 @@ impl Scanner {
|
||||
|
||||
/// Given a socket, scan it self.tries times.
|
||||
/// Turns the address into a SocketAddr
|
||||
/// Deals with the <result> type
|
||||
/// Deals with the `<result>` type
|
||||
/// If it experiences error ErrorKind::Other then too many files are open and it Panics!
|
||||
/// Else any other error, it returns the error in Result as a string
|
||||
/// If no errors occur, it returns the port number in Result to signify the port is open.
|
||||
/// This function mainly deals with the logic of Results handling.
|
||||
/// # Example
|
||||
///
|
||||
/// self.scan_socket(socket)
|
||||
/// ```compile_fail
|
||||
/// scanner.scan_socket(socket)
|
||||
/// ```
|
||||
///
|
||||
/// Note: `self` must contain `self.ip`.
|
||||
async fn scan_socket(&self, socket: SocketAddr) -> io::Result<SocketAddr> {
|
||||
@@ -125,7 +144,7 @@ impl Scanner {
|
||||
}
|
||||
if !self.greppable {
|
||||
if self.accessible {
|
||||
println!("Open {}", socket.to_string());
|
||||
println!("Open {socket}");
|
||||
} else {
|
||||
println!("Open {}", socket.to_string().purple());
|
||||
}
|
||||
@@ -137,9 +156,7 @@ impl Scanner {
|
||||
Err(e) => {
|
||||
let mut error_string = e.to_string();
|
||||
|
||||
if error_string.to_lowercase().contains("too many open files") {
|
||||
panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
|
||||
}
|
||||
assert!(!error_string.to_lowercase().contains("too many open files"), "Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
|
||||
|
||||
if nr_try == tries {
|
||||
error_string.push(' ');
|
||||
@@ -155,13 +172,16 @@ impl Scanner {
|
||||
/// 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
|
||||
/// ```compile_fail
|
||||
/// # use std::net::{IpAddr, Ipv6Addr, SocketAddr};
|
||||
/// 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);
|
||||
/// scanner.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(
|
||||
@@ -176,7 +196,7 @@ impl Scanner {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{PortRange, ScanOrder};
|
||||
use crate::input::{PortRange, ScanOrder};
|
||||
use async_std::task::block_on;
|
||||
use std::{net::IpAddr, time::Duration};
|
||||
|
||||
@@ -197,6 +217,7 @@ mod tests {
|
||||
true,
|
||||
strategy,
|
||||
true,
|
||||
vec![9000],
|
||||
);
|
||||
block_on(scanner.run());
|
||||
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
|
||||
@@ -219,6 +240,7 @@ mod tests {
|
||||
true,
|
||||
strategy,
|
||||
true,
|
||||
vec![9000],
|
||||
);
|
||||
block_on(scanner.run());
|
||||
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
|
||||
@@ -240,6 +262,7 @@ mod tests {
|
||||
true,
|
||||
strategy,
|
||||
true,
|
||||
vec![9000],
|
||||
);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
@@ -260,6 +283,7 @@ mod tests {
|
||||
true,
|
||||
strategy,
|
||||
true,
|
||||
vec![9000],
|
||||
);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
@@ -283,6 +307,7 @@ mod tests {
|
||||
true,
|
||||
strategy,
|
||||
true,
|
||||
vec![9000],
|
||||
);
|
||||
block_on(scanner.run());
|
||||
assert_eq!(1, 1);
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct SocketIterator<'s> {
|
||||
/// 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
|
||||
/// without 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 {
|
||||
@@ -29,6 +29,7 @@ impl<'s> SocketIterator<'s> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::doc_link_with_quotes)]
|
||||
impl<'s> Iterator for SocketIterator<'s> {
|
||||
type Item = SocketAddr;
|
||||
|
||||
@@ -43,10 +44,9 @@ impl<'s> Iterator for SocketIterator<'s> {
|
||||
/// 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)),
|
||||
}
|
||||
self.product_it
|
||||
.next()
|
||||
.map(|(port, ip)| SocketAddr::new(*ip, *port))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+82
-54
@@ -1,52 +1,83 @@
|
||||
//! Scripting engine to run scripts based on tags.
|
||||
//! Scripting Engine to run scripts based on tags.
|
||||
//!
|
||||
//! This module serves to filter and run the scripts selected by the user.
|
||||
//!
|
||||
//! A new commandline and configuration file option was added.
|
||||
//!
|
||||
//! --scripts
|
||||
//! ## `--scripts`
|
||||
//!
|
||||
//! default
|
||||
//! This is the default behavior, like as it was from the beginning of RustScan.
|
||||
//! The user do not have to chose anything for this. This is the only script embedded in RustScan running as default.
|
||||
//! ### `default`
|
||||
//!
|
||||
//! none
|
||||
//! The user have to use the --scripts none commandline argument or scripts = "none" in the config file.
|
||||
//! None of the scripts will run, this replaces the removed --no-nmap option.
|
||||
//! This is the default behavior, like as it was from the beginning of RustScan.
|
||||
//!
|
||||
//! custom
|
||||
//! The user have to use the --scripts custom commandline argument or scripts = "custom" in the config file.
|
||||
//! Rustscan will look for the script configuration file in the user's home dir: home_dir/.rustscan_scripts.toml
|
||||
//! The config file have 3 optional fields, tag, developer and port. Just the tag field will be used forther in the process.
|
||||
//! RustScan will also look for available scripts in the user's home dir: home_dir/.rustscan_scripts
|
||||
//! and will try to read all the files, and parse them into a vector of ScriptFiles.
|
||||
//! Filtering on tags means the tags found in the rustscan_scripts.toml file will also have to be present in the Scriptfile,
|
||||
//! otherwise the script will not be selected.
|
||||
//! All of the rustscan_script.toml tags have to be present at minimum in a Scriptfile to get selected, but can be also more.
|
||||
//! The user do not have to chose anything for this. This is the only script
|
||||
//! embedded in RustScan running as default.
|
||||
//!
|
||||
//! ### `none`
|
||||
//!
|
||||
//! The user have to use the `--scripts none` commandline argument or `scripts =
|
||||
//! "none"` in the config file.
|
||||
//!
|
||||
//! None of the scripts will run, this replaces the removed `--no-nmap` option.
|
||||
//!
|
||||
//! ### `custom`
|
||||
//!
|
||||
//! The user have to use the `--scripts custom` commandline argument or
|
||||
//! `scripts = "custom"` in the config file.
|
||||
//!
|
||||
//! RustScan will look for the script configuration file in the user's home
|
||||
//! dir: `home_dir/.rustscan_scripts.toml`
|
||||
//!
|
||||
//! The config file have 3 optional fields: `tag`, `developer` and `port`. Just
|
||||
//! the `tag` field will be used forther in the process.
|
||||
//!
|
||||
//! RustScan will also look for available scripts in the user's home dir:
|
||||
//! `home_dir/.rustscan_scripts` and will try to read all the files, and parse
|
||||
//! them into a vector of [`ScriptFile`].
|
||||
//!
|
||||
//! Filtering on tags means the tags found in the `rustscan_scripts.toml` file
|
||||
//! will also have to be present in the [`ScriptFile`], otherwise the script
|
||||
//! will not be selected.
|
||||
//!
|
||||
//! All of the `rustscan_script.toml` tags have to be present at minimum in a
|
||||
//! [`ScriptFile`] to get selected, but can be also more.
|
||||
//!
|
||||
//! Config file example:
|
||||
//! fixtures/test_rustscan_scripts.toml
|
||||
//!
|
||||
//! - `fixtures/test_rustscan_scripts.toml`
|
||||
//!
|
||||
//! Script file examples:
|
||||
//! fixtures/test_script.py
|
||||
//! fixtures/test_script.pl
|
||||
//! fixtures/test_script.sh
|
||||
//! fixtures/test_script.txt
|
||||
//!
|
||||
//! call_format in script files can be of 2 variants.
|
||||
//! One is where all of the possible tags {{script}} {{ip}} {{port}} are there.
|
||||
//! The {{script}} part will be replaced with the scriptfile full path gathered while parsing available scripts.
|
||||
//! The {{ip}} part will be replaced with the ip we got from the scan.
|
||||
//! The {{port}} part will be reaplced with the ports separated with the ports_separator found in the script file
|
||||
//! - `fixtures/test_script.py`
|
||||
//! - `fixtures/test_script.pl`
|
||||
//! - `fixtures/test_script.sh`
|
||||
//! - `fixtures/test_script.txt`
|
||||
//!
|
||||
//! And when there is only {{ip}} and {{port}} is in the format, ony those will be replaced with the arguments from the scan.
|
||||
//! This makes it easy to run a system installed command like nmap, and give any kind of arguments to it.
|
||||
//! `call_format` in script files can be of 2 variants:
|
||||
//!
|
||||
//! If the format is different, the script will be silently discarded and will not run. With the Debug option it's possible to see where it goes wrong.
|
||||
//! One is where all of the possible tags `{{script}}`, `{{ip}}` and `{{port}}`
|
||||
//! are there.
|
||||
//!
|
||||
//! - The `{{script}}` part will be replaced with the scriptfile full path
|
||||
//! gathered while parsing available scripts.
|
||||
//! - The `{{ip}}` part will be replaced with the ip we got from the scan.
|
||||
//! - The `{{port}}` part will be reaplced with the ports separated with the
|
||||
//! `ports_separator` found in the script file
|
||||
//!
|
||||
//! And when there is only `{{ip}}` and `{{port}}` is in the format, only those
|
||||
//! will be replaced with the arguments from the scan.
|
||||
//!
|
||||
//! This makes it easy to run a system installed command like `nmap`, and give
|
||||
//! any kind of arguments to it.
|
||||
//!
|
||||
//! If the format is different, the script will be silently discarded and will
|
||||
//! not run. With the `Debug` option it's possible to see where it goes wrong.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use crate::input::ScriptsRequired;
|
||||
use anyhow::{anyhow, Result};
|
||||
use log::debug;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryInto;
|
||||
@@ -72,14 +103,13 @@ pub fn init_scripts(scripts: ScriptsRequired) -> Result<Vec<ScriptFile>> {
|
||||
ScriptsRequired::None => Ok(scripts_to_run),
|
||||
ScriptsRequired::Default => {
|
||||
let default_script =
|
||||
toml::from_str::<ScriptFile>(&DEFAULT).expect("Failed to parse Script file.");
|
||||
toml::from_str::<ScriptFile>(DEFAULT).expect("Failed to parse Script file.");
|
||||
scripts_to_run.push(default_script);
|
||||
Ok(scripts_to_run)
|
||||
}
|
||||
ScriptsRequired::Custom => {
|
||||
let scripts_dir_base = match dirs::home_dir() {
|
||||
Some(dir) => dir,
|
||||
None => return Err(anyhow!("Could not infer scripts path.")),
|
||||
let Some(scripts_dir_base) = dirs::home_dir() else {
|
||||
return Err(anyhow!("Could not infer scripts path."));
|
||||
};
|
||||
let script_paths = match find_scripts(scripts_dir_base) {
|
||||
Ok(script_paths) => script_paths,
|
||||
@@ -105,7 +135,7 @@ pub fn init_scripts(scripts: ScriptsRequired) -> Result<Vec<ScriptFile>> {
|
||||
let script_hashset: HashSet<String> =
|
||||
script.tags.clone().unwrap().into_iter().collect();
|
||||
if config_hashset.is_subset(&script_hashset) {
|
||||
scripts_to_run.push(script.to_owned());
|
||||
scripts_to_run.push(script.clone());
|
||||
} else {
|
||||
debug!(
|
||||
"\nScript tags does not match config tags {:?} {}",
|
||||
@@ -134,6 +164,7 @@ pub fn parse_scripts(scripts: Vec<PathBuf>) -> Vec<ScriptFile> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Script {
|
||||
// Path to the script itself.
|
||||
path: Option<PathBuf>,
|
||||
@@ -233,16 +264,14 @@ impl Script {
|
||||
}
|
||||
debug!("\nScript format to run {}", to_run);
|
||||
|
||||
let arguments = shell_words::split(&to_run).expect("Failed to parse script arguments");
|
||||
|
||||
execute_script(arguments)
|
||||
execute_script(&to_run)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
fn execute_script(mut arguments: Vec<String>) -> Result<String> {
|
||||
debug!("\nScript arguments vec: {:?}", &arguments);
|
||||
let process = Exec::cmd(&arguments.remove(0)).args(&arguments);
|
||||
fn execute_script(script: &str) -> Result<String> {
|
||||
debug!("\nScript arguments {}", script);
|
||||
let process = Exec::shell(script);
|
||||
match process.capture() {
|
||||
Ok(c) => {
|
||||
let es = match c.exit_status {
|
||||
@@ -293,16 +322,14 @@ impl ScriptFile {
|
||||
let real_path = script.clone();
|
||||
let mut lines_buf = String::new();
|
||||
if let Ok(file) = File::open(script) {
|
||||
for line in io::BufReader::new(file).lines().skip(1) {
|
||||
if let Ok(mut line) = line {
|
||||
if line.starts_with('#') {
|
||||
line.retain(|c| c != '#');
|
||||
line = line.trim().to_string();
|
||||
line.push('\n');
|
||||
lines_buf.push_str(&line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
for mut line in io::BufReader::new(file).lines().skip(1).flatten() {
|
||||
if line.starts_with('#') {
|
||||
line.retain(|c| c != '#');
|
||||
line = line.trim().to_string();
|
||||
line.push('\n');
|
||||
lines_buf.push_str(&line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -336,9 +363,8 @@ pub struct ScriptConfig {
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl ScriptConfig {
|
||||
pub fn read_config() -> Result<ScriptConfig> {
|
||||
let mut home_dir = match dirs::home_dir() {
|
||||
Some(dir) => dir,
|
||||
None => return Err(anyhow!("Could not infer ScriptConfig path.")),
|
||||
let Some(mut home_dir) = dirs::home_dir() else {
|
||||
return Err(anyhow!("Could not infer ScriptConfig path."));
|
||||
};
|
||||
home_dir.push(".rustscan_scripts.toml");
|
||||
|
||||
@@ -435,6 +461,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn run_bash_script() {
|
||||
let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.sh".into()).unwrap();
|
||||
let script: Script = into_script(script_f);
|
||||
@@ -456,6 +483,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn run_perl_script() {
|
||||
let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.pl".into()).unwrap();
|
||||
let script: Script = into_script(script_f);
|
||||
|
||||
+34
-14
@@ -1,21 +1,20 @@
|
||||
//! Utilities for terminal output during scanning.
|
||||
|
||||
/// 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);
|
||||
println!("{} {}", ansi_term::Colour::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);
|
||||
println!("{} {}", ansi_term::Colour::Red.bold().paint("[!]"), $name);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -24,18 +23,16 @@ macro_rules! warning {
|
||||
#[macro_export]
|
||||
macro_rules! detail {
|
||||
($name:expr) => {
|
||||
use ansi_term::Colour::Blue;
|
||||
println!("{} {}", Blue.bold().paint("[~]"), $name);
|
||||
println!("{} {}", ansi_term::Colour::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);
|
||||
println!("{} {}", ansi_term::Colour::Blue.bold().paint("[~]"), $name);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -44,18 +41,24 @@ macro_rules! detail {
|
||||
#[macro_export]
|
||||
macro_rules! output {
|
||||
($name:expr) => {
|
||||
use ansi_term::Colour::RGB;
|
||||
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
|
||||
println!(
|
||||
"{} {}",
|
||||
RGansi_term::Colour::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);
|
||||
println!(
|
||||
"{} {}",
|
||||
ansi_term::Colour::RGB(0, 255, 9).bold().paint("[>]"),
|
||||
$name
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -72,10 +75,27 @@ macro_rules! funny_opening {
|
||||
"Real hackers hack time ⌛",
|
||||
"Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan",
|
||||
"😵 https://admin.tryhackme.com",
|
||||
"0day was here ♥",
|
||||
"I don't always scan ports, but when I do, I prefer RustScan.",
|
||||
"RustScan: Where scanning meets swagging. 😎",
|
||||
"To scan or not to scan? That is the question.",
|
||||
"RustScan: Because guessing isn't hacking.",
|
||||
"Scanning ports like it's my full-time job. Wait, it is.",
|
||||
"Open ports, closed hearts.",
|
||||
"I scanned my computer so many times, it thinks we're dating.",
|
||||
"Port scanning: Making networking exciting since... whenever.",
|
||||
"You miss 100% of the ports you don't scan. - RustScan",
|
||||
"Breaking and entering... into the world of open ports.",
|
||||
"TCP handshake? More like a friendly high-five!",
|
||||
"Scanning ports: The virtual equivalent of knocking on doors.",
|
||||
"RustScan: Making sure 'closed' isn't just a state of mind.",
|
||||
"Port scanning: Because every port has a story to tell.",
|
||||
"I scanned ports so fast, even my computer was surprised.",
|
||||
"RustScan: Where '404 Not Found' meets '200 OK'.",
|
||||
"RustScan: Exploring the digital landscape, one IP at a time.",
|
||||
];
|
||||
let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap();
|
||||
|
||||
println!("{}\n", random_quote);
|
||||
// println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -35,7 +35,7 @@ fn run_rustscan_with_timeout(args: &[&str], timeout: Duration) {
|
||||
if tries == 0 {
|
||||
// child hasn't exited yet
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap().code();
|
||||
child.wait().unwrap().code().unwrap();
|
||||
panic!("Timeout while running command");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user