Compare commits

..
Author SHA1 Message Date
bee cb1d3ffeae run just 2025-03-04 21:15:56 +00:00
bee 74a9e3e89e Add custom script directory 2025-03-04 21:12:33 +00:00
github-actions[bot] c123457734 Merge pull request #756 from RustScan/dependabot/cargo/clap-4.5.31
build(deps): bump clap from 4.5.30 to 4.5.31
2025-02-25 09:57:21 +00:00
dependabot[bot] 727a64d934 build(deps): bump clap from 4.5.30 to 4.5.31
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.30 to 4.5.31.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.30...v4.5.31)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-02-25 09:57:05 +00:00
bee b1f008a9c9 2.4.1 2025-02-23 15:03:57 +00:00
Autumn (Bee)andbee b9d5cee67a Auto merge dependabot PRs (#755)
This pr:
1. checks if the user == dependabot[bot]
2. if all CI passes, auto approve and merge the PR

Co-authored-by: bee <[email protected]>
2025-02-23 14:40:04 +00:00
fabianbees cbd0369dbd remove generated.rs file from git (#750) 2025-02-21 19:51:47 +00:00
Roger G. Coram f1ef3be688 feat: preserve IP address order (#749)
as part of #651, IP addresses are deduplicated; as this uses a
`BTreeSet`, the original order is not necessarily retained.

use
[`retain()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.retain)
to remove duplicated and/or excluded IPs, mutating the original list of
IPs but preserving the original order.
2025-02-15 15:34:56 +00:00
Roger G. Coram 71091bfe86 Merge pull request #746 from fabianbees/fix/nmap-payloads-hex-conversion-error
fix: Statically generated payloads (nmap-payloads) for UDP scanning
2025-02-12 11:40:06 +00:00
Fabian Bees e797458f43 fix: Statically generated payloads (nmap-payloads) for UDP scanning 2025-02-12 12:36:05 +01:00
Roger G. Coram 2b409c9d33 Merge pull request #748 from RustScan/dependabot/cargo/hickory-proto-0.24.3
build(deps): bump hickory-proto from 0.24.1 to 0.24.3
2025-02-12 09:10:41 +00:00
Roger G. Coram ef8852aeff Merge pull request #747 from RustScan/dependabot/cargo/hickory-resolver-0.24.3
build(deps): bump hickory-resolver from 0.24.2 to 0.24.3
2025-02-12 09:10:11 +00:00
dependabot[bot] 98794d8187 build(deps): bump hickory-proto from 0.24.1 to 0.24.3
Bumps [hickory-proto](https://github.com/hickory-dns/hickory-dns) from 0.24.1 to 0.24.3.
- [Release notes](https://github.com/hickory-dns/hickory-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.24.3/CHANGELOG.md)
- [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.1...v0.24.3)

---
updated-dependencies:
- dependency-name: hickory-proto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-02-10 17:59:30 +00:00
dependabot[bot] 9221567d25 build(deps): bump hickory-resolver from 0.24.2 to 0.24.3
Bumps [hickory-resolver](https://github.com/hickory-dns/hickory-dns) from 0.24.2 to 0.24.3.
- [Release notes](https://github.com/hickory-dns/hickory-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.24.3/CHANGELOG.md)
- [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.2...v0.24.3)

---
updated-dependencies:
- dependency-name: hickory-resolver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-02-10 09:27:17 +00:00
fabianbees c5e518889e fix issue 663: UDP scan returns port as OPEN, when timed-out (#737) 2025-02-08 07:50:30 +00:00
Roger G. Coram 981c1741fe Merge pull request #744 from RustScan/dependabot/cargo/once_cell-1.20.3
build(deps): bump once_cell from 1.20.2 to 1.20.3
2025-02-07 11:22:56 +00:00
Roger G. Coram a88d311ecf Merge pull request #743 from RustScan/dependabot/cargo/toml-0.8.20
build(deps): bump toml from 0.8.19 to 0.8.20
2025-02-07 11:21:52 +00:00
Roger G. Coram c782b07041 Merge pull request #742 from RustScan/dependabot/cargo/wait-timeout-0.2.1
build(deps): bump wait-timeout from 0.2.0 to 0.2.1
2025-02-07 11:21:25 +00:00
dependabot[bot] 971f2523f5 build(deps): bump once_cell from 1.20.2 to 1.20.3
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.2 to 1.20.3.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.2...v1.20.3)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-02-07 09:08:57 +00:00
dependabot[bot] d1cd45ec29 build(deps): bump toml from 0.8.19 to 0.8.20
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.19 to 0.8.20.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.19...toml-v0.8.20)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-02-06 09:18:01 +00:00
dependabot[bot] a6213bc1a3 build(deps): bump wait-timeout from 0.2.0 to 0.2.1
Bumps [wait-timeout](https://github.com/alexcrichton/wait-timeout) from 0.2.0 to 0.2.1.
- [Commits](https://github.com/alexcrichton/wait-timeout/compare/0.2.0...0.2.1)

---
updated-dependencies:
- dependency-name: wait-timeout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-02-04 09:19:05 +00:00
Roger G. Coram e1689e506c Merge pull request #739 from RustScan/dependabot/cargo/rand-0.9.0
build(deps): bump rand from 0.8.5 to 0.9.0
2025-02-03 17:49:54 +00:00
PsypherPunk 85f4eb5a30 fix: update as per rand from 0.9.0
- `thread_rng` is now `rng`
- `gen_range` is now `random_range`
- `SliceRandom` is now `IndexedRandom`
2025-02-03 17:44:16 +00:00
dependabot[bot] 34744424d7 build(deps): bump rand from 0.8.5 to 0.9.0
Bumps [rand](https://github.com/rust-random/rand) from 0.8.5 to 0.9.0.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.8.5...0.9.0)

---
updated-dependencies:
- dependency-name: rand
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-02-03 17:44:16 +00:00
Roger G. Coram ba5d51788a fix: correct clippy warnings (#734) 2025-01-29 21:18:40 +00:00
Roger G. Coram b17b733580 docs: update link to build status badge (#730)
update the link as per the
[docs.](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/adding-a-workflow-status-badge#using-the-branch-parameter).
2025-01-23 19:25:16 +00:00
Roger G. Coram 3b06a34885 Merge pull request #719 from thannaske/feature-exclude-addresses
feat: add address exclusion flag (#718)
2025-01-20 13:09:27 +00:00
Roger G. Coram e3f129e6d1 Merge pull request #720 from sdushantha/no-banner
Added --no-banner option
2025-01-20 11:32:55 +00:00
Roger G. Coram 604ff2acf0 Merge pull request #732 from RustScan/dependabot/cargo/log-0.4.25
build(deps): bump log from 0.4.22 to 0.4.25
2025-01-19 09:27:43 +00:00
dependabot[bot] a8c52917c3 build(deps): bump log from 0.4.22 to 0.4.25
Bumps [log](https://github.com/rust-lang/log) from 0.4.22 to 0.4.25.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.22...0.4.25)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-01-15 09:20:48 +00:00
Roger G. Coram cbf0f94ca1 Merge pull request #729 from RustScan/dependabot/cargo/dirs-6.0.0
build(deps): bump dirs from 5.0.1 to 6.0.0
2025-01-13 10:18:31 +00:00
Roger G. Coram 4a7101b2f5 Merge pull request #727 from RustScan/dependabot/cargo/colored-3.0.0
build(deps): bump colored from 2.2.0 to 3.0.0
2025-01-13 10:12:16 +00:00
dependabot[bot] 07e7b30f51 build(deps): bump dirs from 5.0.1 to 6.0.0
Bumps [dirs](https://github.com/soc/dirs-rs) from 5.0.1 to 6.0.0.
- [Commits](https://github.com/soc/dirs-rs/commits)

---
updated-dependencies:
- dependency-name: dirs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-01-13 09:29:40 +00:00
dependabot[bot] 43c43ae91d build(deps): bump colored from 2.2.0 to 3.0.0
Bumps [colored](https://github.com/mackwic/colored) from 2.2.0 to 3.0.0.
- [Release notes](https://github.com/mackwic/colored/releases)
- [Changelog](https://github.com/colored-rs/colored/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mackwic/colored/compare/v2.2.0...v3.0.0)

---
updated-dependencies:
- dependency-name: colored
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-01-08 09:37:03 +00:00
伊诺 4f19e42ebb Update input.rs (#726)
Updated a TOML syntax error  
scan_order = "Serial"
2025-01-03 10:27:03 +00:00
Roger G. Coram f1810702ab Merge pull request #725 from RustScan/dependabot/cargo/itertools-0.14.0
build(deps): bump itertools from 0.13.0 to 0.14.0
2025-01-02 14:07:07 +00:00
dependabot[bot] 0c0433d628 build(deps): bump itertools from 0.13.0 to 0.14.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.13.0 to 0.14.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-01-01 09:59:53 +00:00
Roger G. Coram de98d0044e Merge pull request #721 from RustScan/dependabot/cargo/env_logger-0.11.6
build(deps): bump env_logger from 0.11.5 to 0.11.6
2024-12-23 14:50:53 +00:00
dependabot[bot] 55e388241e build(deps): bump env_logger from 0.11.5 to 0.11.6
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.5 to 0.11.6.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.5...v0.11.6)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-12-23 10:01:33 +00:00
Siddharth Dushantha d969699fff Added --no-banner option 2024-12-22 13:04:36 +01:00
Roger G. Coram 4542798c7b build: remove Dockerfile (#712)
no longer explicitly supported.
2024-12-22 10:55:21 +00:00
Roger G. Coram 69629c4fc3 chore: fix clippy lists in tests/benchmarks (#717)
* chore: fix clippy lists in tests/benchmarks

* build: extend clippy checks to tests
2024-12-22 10:54:59 +00:00
t.hannaske 8676de1c19 Adding address exclusion option with test coverage 2024-12-22 09:51:14 +01:00
Roger G. Coram 90708aec1d Merge pull request #716 from RustScan/dependabot/cargo/colored-2.2.0
build(deps): bump colored from 2.1.0 to 2.2.0
2024-12-17 08:24:22 +00:00
Roger G. Coram 094a6f00e7 docs: remove attribution in comments (#714)
contributors are tracked within the repository and attribution remains
within the project's Git history.
2024-12-16 17:54:33 +00:00
dependabot[bot] 840daac9d2 build(deps): bump colored from 2.1.0 to 2.2.0
Bumps [colored](https://github.com/mackwic/colored) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/mackwic/colored/releases)
- [Changelog](https://github.com/colored-rs/colored/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mackwic/colored/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: colored
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-12-16 09:11:35 +00:00
Roger G. Coram b171ed7c88 Merge pull request #715 from RustScan/dependabot/cargo/hickory-resolver-0.24.2
build(deps): bump hickory-resolver from 0.24.1 to 0.24.2
2024-12-11 13:22:51 +00:00
dependabot[bot] 5a9cfeeb40 build(deps): bump hickory-resolver from 0.24.1 to 0.24.2
Bumps [hickory-resolver](https://github.com/hickory-dns/hickory-dns) from 0.24.1 to 0.24.2.
- [Release notes](https://github.com/hickory-dns/hickory-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.24.2/CHANGELOG.md)
- [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.1...v0.24.2)

---
updated-dependencies:
- dependency-name: hickory-resolver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-12-11 10:02:17 +00:00
Roger G. Coram 6a25fb1d98 Merge pull request #709 from Integral-Tech/replace-format
refactor: replace format! with concat! for string literals
2024-12-08 14:39:08 +00:00
Integral bddda4d2c8 refactor: skip using format! macro 2024-12-07 23:24:42 +08:00
Integral 75c7d6215b Fix CI 2024-12-07 23:17:00 +08:00
d9dec00197 benchmark code for rustscan (#705)
* benchmark

* Change duration of each individual port scan to 10ms

* test: extend UDP benchmark measurement time

* benching helper funcs

* Broken benchmarking code

* Add complete benchmarking for most functions

---------

Co-authored-by: bee <[email protected]>
Co-authored-by: PsypherPunk <[email protected]>
2024-11-26 12:57:52 +00:00
Roger G. Coram 1634bab1b4 Merge pull request #703 from RustScan/bee-add-benches
Add benchmarks for UDP and TCP
2024-11-25 20:21:39 +00:00
PsypherPunk c58c389b1e Merge branch 'master' into bee-add-benches 2024-11-25 20:16:11 +00:00
Roger G. Coram d150802c04 Merge pull request #702 from RustScan/bee-update-lib
Add documentation to RustScan library
2024-11-25 20:14:34 +00:00
PsypherPunk dd9e075e56 Merge branch 'master' into bee-update-lib 2024-11-25 20:10:34 +00:00
Roger G. Coram d3229ff9a9 Merge pull request #701 from RustScan/bee-fix-tests
Fix tests
2024-11-25 17:51:19 +00:00
bee aae43ef47e Add benchmarks for UDP and TCP
This CI will fail initially due to an issue fixed in another PR.

This runs the udp scan and tcp scan 100 times and gets an average measure.

Currently these are:

```
portscan tcp            time:   [38.941 ms 39.659 ms 40.397 ms]
                        change: [+62486512% +64310488% +66078003%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild

Benchmarking portscan udp: Warming up for 3.0000 s
Warning: Unable to complete 100 samples in 5.0s. You may wish to increase target time to 5.9s, or reduce sample count to 80.
portscan udp            time:   [58.419 ms 59.596 ms 60.775 ms]
                        change: [+93198493% +96139097% +98913211%] (p = 0.00 < 0.05)
                        Performance has regressed.
```

Notes:
* TCP scan takes 40ms on average
* UDP scan does not complete all 100 samples, as it's a bit slow. Takes 60ms on average.
* I ran these previously for testing so "performance has regressed" can be ignored
* We scan 1k ports because 64k ports over 100 samples is way too many.
2024-11-25 10:51:43 +00:00
Autumn (Bee) 387edc7b05 Add port strat 2024-11-25 10:05:43 +00:00
Autumn (Bee) bb01c3b5ee Add documentation to RustScan library
Our library is sorely lacking docs. I read this https://docs.rs/rustscan/latest/rustscan/ and had no idea what the arguments are. These comments should hopefully show up :)
2024-11-25 10:03:38 +00:00
bee 89541eddfe Fix tests 2024-11-25 09:56:13 +00:00
bee 45537cf6cd Update readme 2024-11-25 09:51:41 +00:00
22 changed files with 1029 additions and 3465 deletions

No files matched your search

+2 -4
View File
@@ -1,8 +1,6 @@
name: CD Pipeline
on:
push:
tags:
- '*'
on: [push]
jobs:
build-nix:
+19
View File
@@ -0,0 +1,19 @@
name: Dependabot Auto-Merge
on: pull_request
permissions:
contents: write
pull-requests: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Enable auto-merge for Dependabot PRs
run: |
gh pr review --approve "$PR_URL"
gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2
View File
@@ -1,6 +1,8 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# generated file by build.rs for mapping nmap-payloads to rust
src/generated.rs
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Generated
+658 -322
View File
File diff suppressed because it is too large. Load diff
+11 -11
View File
@@ -1,6 +1,6 @@
[package]
name = "rustscan"
version = "2.3.0"
version = "2.4.1"
authors = ["Autumn <[email protected]>"]
edition = "2018"
description = "Faster Nmap Scanning with Rust"
@@ -17,28 +17,28 @@ exclude = [
]
[dependencies]
clap = { version = "4.5.13", features = ["derive", "wrap_help"] }
colored = "2.1.0"
clap = { version = "4.5.31", features = ["derive", "wrap_help"] }
colored = "3.0.0"
async-std = "1.13.0"
futures = "0.3"
rlimit = "0.10.2"
log = "0.4.22"
env_logger = "0.11.5"
log = "0.4.25"
env_logger = "0.11.6"
anstream = "=0.6.18"
dirs = "5.0.1"
dirs = "6.0.0"
gcd = "2.0.1"
rand = "0.8.5"
rand = "0.9.0"
colorful = "0.3.2"
ansi_term = "0.12.1"
toml = "0.8.19"
toml = "0.8.20"
serde = "1.0.124"
serde_derive = "1.0.116"
cidr-utils = "0.6.1"
itertools = "0.13.0"
hickory-resolver = { version = "0.24.0", features = ["dns-over-rustls"] }
itertools = "0.14.0"
hickory-resolver = { version = "0.24.3", features = ["dns-over-rustls"] }
anyhow = "1.0.40"
text_placeholder = { version = "0.5", features = ["struct_context"] }
once_cell = "1.20.2"
once_cell = "1.20.3"
[dev-dependencies]
parameterized = "2.0.0"
-25
View File
@@ -1,25 +0,0 @@
# 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 /app/rustscan
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
RUN cargo build --release
# Release Stage
FROM alpine:3.20.3 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 ca-certificates bind-tools
USER rustscan
COPY --from=builder /app/rustscan/target/release/rustscan /usr/local/bin/rustscan
ENTRYPOINT [ "/usr/local/bin/rustscan" ]
+5 -1
View File
@@ -19,6 +19,10 @@ The Modern Port Scanner. **Find ports quickly (3 seconds at its fastest)**. Run
# 🛠️ Installation
You can install RustScan's binary from our [releases page](https://github.com/RustScan/RustScan/releases).
We would prefer you to install with a package manager so it is tested and works for your system.
RustScan is in many repositories already. Install it with whatever tools you wish:
[![Packaging status](https://repology.org/badge/vertical-allrepos/rustscan.svg)](https://repology.org/project/rustscan/versions)
@@ -228,4 +232,4 @@ This project follows the [all-contributors](https://github.com/all-contributors/
[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/Build/badge.svg?branch=master
[badge-6]: https://github.com/RustScan/RustScan/actions/workflows/build.yml/badge.svg?branch=master
+72 -9
View File
@@ -1,16 +1,79 @@
use criterion::{criterion_group, criterion_main, Criterion};
use std::hint::black_box;
use async_std::task::block_on;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rustscan::input::{PortRange, ScanOrder};
use rustscan::port_strategy::PortStrategy;
use rustscan::scanner::Scanner;
use std::net::IpAddr;
use std::time::Duration;
fn fibonacci(n: u64) -> u64 {
match n {
0 => 1,
1 => 1,
n => fibonacci(n - 1) + fibonacci(n - 2),
}
fn portscan_tcp(scanner: &Scanner) {
let _scan_result = block_on(scanner.run());
}
fn portscan_udp(scanner: &Scanner) {
let _scan_result = block_on(scanner.run());
}
fn bench_address() {
let _addrs = ["127.0.0.1".parse::<IpAddr>().unwrap()];
}
fn bench_port_strategy() {
let range = PortRange {
start: 1,
end: 1_000,
};
let _strategy = PortStrategy::pick(&Some(range.clone()), None, ScanOrder::Serial);
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 1,
end: 1_000,
};
let strategy_tcp = PortStrategy::pick(&Some(range.clone()), None, ScanOrder::Serial);
let strategy_udp = PortStrategy::pick(&Some(range.clone()), None, ScanOrder::Serial);
let scanner_tcp = Scanner::new(
&addrs,
10,
Duration::from_millis(10),
1,
false,
strategy_tcp,
true,
vec![],
false,
);
c.bench_function("portscan tcp", |b| {
b.iter(|| portscan_tcp(black_box(&scanner_tcp)))
});
let scanner_udp = Scanner::new(
&addrs,
10,
Duration::from_millis(10),
1,
false,
strategy_udp,
true,
vec![],
true,
);
let mut udp_group = c.benchmark_group("portscan udp");
udp_group.measurement_time(Duration::from_secs(20));
udp_group.bench_function("portscan udp", |b| {
b.iter(|| portscan_udp(black_box(&scanner_udp)))
});
udp_group.finish();
// Benching helper functions
c.bench_function("parse address", |b| b.iter(bench_address));
c.bench_function("port strategy", |b| b.iter(bench_port_strategy));
}
criterion_group!(benches, criterion_benchmark);
+1 -1
View File
@@ -194,7 +194,7 @@ fn parser(payload: &str) -> Vec<u8> {
for (idx, char) in payload.chars().enumerate() {
if char == '\\' && payload.chars().nth(idx + 1) == Some('x') {
continue;
} else if char.is_ascii_digit() {
} else if char.is_ascii_hexdigit() {
tmp_str.push(char);
if tmp_str.len() == 2 {
bytes.push(u8::from_str_radix(&tmp_str, 16).unwrap());
+4
View File
@@ -0,0 +1,4 @@
tags = ["core_approved", "example"]
ports = ["80", "443", "8080"]
developer = ["example", "https://example.org"]
directory = "fixtures/.rustscan_scripts"
+1
View File
@@ -1,6 +1,7 @@
test:
cargo nextest run
cargo clippy -- --deny warnings
cargo clippy --tests -- --deny warnings
cargo fmt --check
cargo doc --workspace --all-features --no-deps --document-private-items
+119 -23
View File
@@ -69,10 +69,19 @@ pub fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
}
}
ips.into_iter()
.collect::<BTreeSet<_>>()
.into_iter()
.collect()
// Finally, craft a list of addresses to be excluded from the scan.
let mut excluded_ips: BTreeSet<IpAddr> = BTreeSet::new();
if let Some(exclude_addresses) = &input.exclude_addresses {
for addr in exclude_addresses {
excluded_ips.extend(parse_address(addr, &backup_resolver));
}
}
// Remove duplicated/excluded IPs.
let mut seen = BTreeSet::new();
ips.retain(|ip| seen.insert(*ip) && !excluded_ips.contains(ip));
ips
}
/// Given a string, parse it as a host, IP address, or CIDR.
@@ -93,7 +102,7 @@ pub fn parse_address(address: &str, resolver: &Resolver) -> Vec<IpAddr> {
.map(|cidr| cidr.iter().map(|c| c.address()).collect())
.ok()
.or_else(|| {
format!("{}:{}", &address, 80)
format!("{}:80", &address)
.to_socket_addrs()
.ok()
.map(|mut iter| vec![iter.next().unwrap().ip()])
@@ -193,8 +202,11 @@ mod tests {
#[test]
fn parse_correct_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()];
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
@@ -209,10 +221,71 @@ mod tests {
);
}
#[test]
fn parse_addresses_with_address_exclusions() {
let opts = Opts {
addresses: vec!["192.168.0.0/30".to_owned()],
exclude_addresses: Some(vec!["192.168.0.1".to_owned()]),
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
ips,
[
Ipv4Addr::new(192, 168, 0, 0),
Ipv4Addr::new(192, 168, 0, 2),
Ipv4Addr::new(192, 168, 0, 3)
]
);
}
#[test]
fn parse_addresses_with_cidr_exclusions() {
let opts = Opts {
addresses: vec!["192.168.0.0/29".to_owned()],
exclude_addresses: Some(vec!["192.168.0.0/30".to_owned()]),
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
ips,
[
Ipv4Addr::new(192, 168, 0, 4),
Ipv4Addr::new(192, 168, 0, 5),
Ipv4Addr::new(192, 168, 0, 6),
Ipv4Addr::new(192, 168, 0, 7),
]
);
}
#[test]
fn parse_addresses_with_incorrect_address_exclusions() {
let opts = Opts {
addresses: vec!["192.168.0.0/30".to_owned()],
exclude_addresses: Some(vec!["192.168.0.1".to_owned()]),
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
ips,
[
Ipv4Addr::new(192, 168, 0, 0),
Ipv4Addr::new(192, 168, 0, 2),
Ipv4Addr::new(192, 168, 0, 3)
]
);
}
#[test]
fn parse_correct_host_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["google.com".to_owned()];
let opts = Opts {
addresses: vec!["google.com".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 1);
@@ -220,8 +293,11 @@ mod tests {
#[test]
fn parse_correct_and_incorrect_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()];
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]);
@@ -229,43 +305,61 @@ mod tests {
#[test]
fn parse_incorrect_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()];
let opts = Opts {
addresses: vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert!(ips.is_empty());
}
#[test]
fn parse_hosts_file_and_incorrect_hosts() {
// Host file contains IP, Hosts, incorrect IPs, incorrect hosts
let mut opts = Opts::default();
opts.addresses = vec!["fixtures/hosts.txt".to_owned()];
let opts = Opts {
addresses: vec!["fixtures/hosts.txt".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 3);
}
#[test]
fn parse_empty_hosts_file() {
// Host file contains IP, Hosts, incorrect IPs, incorrect hosts
let mut opts = Opts::default();
opts.addresses = vec!["fixtures/empty_hosts.txt".to_owned()];
let opts = Opts {
addresses: vec!["fixtures/empty_hosts.txt".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 0);
}
#[test]
fn parse_naughty_host_file() {
// Host file contains IP, Hosts, incorrect IPs, incorrect hosts
let mut opts = Opts::default();
opts.addresses = vec!["fixtures/naughty_string.txt".to_owned()];
let opts = Opts {
addresses: vec!["fixtures/naughty_string.txt".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 0);
}
#[test]
fn parse_duplicate_cidrs() {
let mut opts = Opts::default();
opts.addresses = vec!["79.98.104.0/21".to_owned(), "79.98.104.0/24".to_owned()];
let opts = Opts {
addresses: vec!["79.98.104.0/21".to_owned(), "79.98.104.0/24".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
@@ -285,9 +379,11 @@ mod tests {
#[test]
fn resolver_args_google_dns() {
let mut opts = Opts::default();
// https://developers.google.com/speed/public-dns
opts.resolver = Some("8.8.8.8,8.8.4.4".to_owned());
let opts = Opts {
resolver: Some("8.8.8.8,8.8.4.4".to_owned()),
..Default::default()
};
let resolver = get_resolver(&opts.resolver);
let lookup = resolver.lookup_ip("www.example.com.").unwrap();
-2996
View File
File diff suppressed because it is too large. Load diff
+14 -2
View File
@@ -89,6 +89,10 @@ pub struct Opts {
#[arg(short, long)]
pub no_config: bool,
/// Hide the banner
#[arg(long)]
pub no_banner: bool,
/// Custom path to config file
#[arg(short, long, value_parser)]
pub config_path: Option<PathBuf>,
@@ -151,6 +155,10 @@ pub struct Opts {
#[arg(short, long, value_delimiter = ',')]
pub exclude_ports: Option<Vec<u16>>,
/// A list of comma separated CIDRs, IPs, or hosts to be excluded from scanning.
#[arg(short = 'x', long = "exclude-addresses", value_delimiter = ',')]
pub exclude_addresses: Option<Vec<String>>,
/// UDP scanning mode, finds UDP ports that send back responses
#[arg(long)]
pub udp: bool,
@@ -217,7 +225,7 @@ impl Opts {
self.ports = Some(ports);
}
merge_optional!(range, resolver, ulimit, exclude_ports);
merge_optional!(range, resolver, ulimit, exclude_ports, exclude_addresses);
}
}
@@ -237,10 +245,12 @@ impl Default for Opts {
resolver: None,
scan_order: ScanOrder::Serial,
no_config: true,
no_banner: false,
top: false,
scripts: ScriptsRequired::Default,
config_path: None,
exclude_ports: None,
exclude_addresses: None,
udp: false,
}
}
@@ -266,6 +276,7 @@ pub struct Config {
command: Option<Vec<String>>,
scripts: Option<ScriptsRequired>,
exclude_ports: Option<Vec<u16>>,
exclude_addresses: Option<Vec<String>>,
udp: Option<bool>,
}
@@ -281,7 +292,7 @@ impl Config {
/// addresses = ["127.0.0.1", "127.0.0.1"]
/// ports = [80, 443, 8080]
/// greppable = true
/// scan_order: "Serial"
/// scan_order = "Serial"
/// exclude_ports = [8080, 9090, 80]
/// udp = false
///
@@ -340,6 +351,7 @@ mod tests {
scan_order: Some(ScanOrder::Random),
scripts: None,
exclude_ports: None,
exclude_addresses: None,
udp: Some(false),
}
}
+10 -10
View File
@@ -21,17 +21,17 @@
//! start: 1,
//! end: 1_000,
//! };
//! let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
//! let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); // can be serial, random or manual https://github.com/RustScan/RustScan/blob/master/src/port_strategy/mod.rs
//! let scanner = Scanner::new(
//! &addrs,
//! 10,
//! Duration::from_millis(100),
//! 1,
//! true,
//! strategy,
//! true,
//! vec![9000],
//! false,
//! &addrs, // the addresses to scan
//! 10, // batch_size is how many ports at a time should be scanned
//! Duration::from_millis(100), //T imeout is the time RustScan should wait before declaring a port closed. As datatype Duration.
//! 1, // Tries, how many retries should RustScan do?
//! true, // greppable is whether or not RustScan should print things, or wait until the end to print only the ip
//! strategy, // the port strategy used
//! true, // accessible, should the output be A11Y compliant?
//! vec![9000], // What ports should RustScan exclude?
//! false, // is this a UDP scan?
//! );
//!
//! let scan_result = block_on(scanner.run());
+37 -35
View File
@@ -62,7 +62,7 @@ fn main() {
debug!("Scripts initialized {:?}", &scripts_to_run);
if !opts.greppable && !opts.accessible {
if !opts.greppable && !opts.accessible && !opts.no_banner {
print_opening(&opts);
}
@@ -83,11 +83,6 @@ fn main() {
#[cfg(not(unix))]
let batch_size: u16 = AVERAGE_BATCH_SIZE;
// Added by wasuaje - 01/26/2024:
// exclude_ports is an exclusion port list
//
// Added by brendanglancy - 5/19/2024:
// udp is an option to do a udp scan
let scanner = Scanner::new(
&ips,
batch_size,
@@ -200,22 +195,17 @@ fn main() {
#[allow(clippy::items_after_statements, clippy::needless_raw_string_hashes)]
fn print_opening(opts: &Opts) {
debug!("Printing opening");
let s = format!(
"{}\n{}\n{}\n{}\n{}",
r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-."#,
r#"| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |"#,
r#"| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |"#,
r#"`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'"#,
r#"The Modern Day Port Scanner."#
);
let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner."#;
println!("{}", s.gradient(Color::Green).bold());
let info = format!(
"{}\n{}\n{}\n{}",
r#"________________________________________"#,
r#": http://discord.skerritt.blog :"#,
r#": https://github.com/RustScan/RustScan :"#,
r#" --------------------------------------"#
);
let info = r#"________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------"#;
println!("{}", info.gradient(Color::Yellow).bold());
funny_opening!();
@@ -305,8 +295,10 @@ mod tests {
#[test]
#[cfg(unix)]
fn batch_size_lowered() {
let mut opts = Opts::default();
opts.batch_size = 50_000;
let opts = Opts {
batch_size: 50_000,
..Default::default()
};
let batch_size = infer_batch_size(&opts, 120);
assert!(batch_size < opts.batch_size);
@@ -315,8 +307,10 @@ mod tests {
#[test]
#[cfg(unix)]
fn batch_size_lowered_average_size() {
let mut opts = Opts::default();
opts.batch_size = 50_000;
let opts = Opts {
batch_size: 50_000,
..Default::default()
};
let batch_size = infer_batch_size(&opts, 9_000);
assert!(batch_size == 3_000);
@@ -326,8 +320,10 @@ mod tests {
fn batch_size_equals_ulimit_lowered() {
// because ulimit and batch size are same size, batch size is lowered
// to ULIMIT - 100
let mut opts = Opts::default();
opts.batch_size = 50_000;
let opts = Opts {
batch_size: 50_000,
..Default::default()
};
let batch_size = infer_batch_size(&opts, 5_000);
assert!(batch_size == 4_900);
@@ -336,9 +332,11 @@ mod tests {
#[cfg(unix)]
fn batch_size_adjusted_2000() {
// ulimit == batch_size
let mut opts = Opts::default();
opts.batch_size = 50_000;
opts.ulimit = Some(2_000);
let opts = Opts {
batch_size: 50_000,
ulimit: Some(2_000),
..Default::default()
};
let batch_size = adjust_ulimit_size(&opts);
assert!(batch_size == 2_000);
@@ -347,9 +345,11 @@ mod tests {
#[test]
#[cfg(unix)]
fn test_high_ulimit_no_greppable_mode() {
let mut opts = Opts::default();
opts.batch_size = 10;
opts.greppable = false;
let opts = Opts {
batch_size: 10,
greppable: false,
..Default::default()
};
let batch_size = infer_batch_size(&opts, 1_000_000);
@@ -358,8 +358,10 @@ mod tests {
#[test]
fn test_print_opening_no_panic() {
let mut opts = Opts::default();
opts.ulimit = Some(2_000);
let opts = Opts {
ulimit: Some(2_000),
..Default::default()
};
// print opening should not panic
print_opening(&opts);
}
+2 -2
View File
@@ -1,8 +1,8 @@
//! Provides a means to hold configuration options specifically for port scanning.
mod range_iterator;
use crate::input::{PortRange, ScanOrder};
use rand::rng;
use rand::seq::SliceRandom;
use rand::thread_rng;
use range_iterator::RangeIterator;
/// Represents options of port scanning.
@@ -35,7 +35,7 @@ impl PortStrategy {
}
ScanOrder::Serial => PortStrategy::Manual(ports.unwrap()),
ScanOrder::Random => {
let mut rng = thread_rng();
let mut rng = rng();
let mut ports = ports.unwrap();
ports.shuffle(&mut rng);
PortStrategy::Manual(ports)
+5 -5
View File
@@ -27,8 +27,8 @@ impl RangeIterator {
// Randomly choose a number within the range to be the first
// and assign it as a pick.
let mut rng = rand::thread_rng();
let normalized_first_pick = rng.gen_range(0..normalized_end);
let mut rng = rand::rng();
let normalized_first_pick = rng.random_range(0..normalized_end);
Self {
active: true,
@@ -83,14 +83,14 @@ fn pick_random_coprime(end: u32) -> u32 {
let range_boundary = end / 4;
let lower_range = range_boundary;
let upper_range = end - range_boundary;
let mut rng = rand::thread_rng();
let mut candidate = rng.gen_range(lower_range..upper_range);
let mut rng = rand::rng();
let mut candidate = rng.random_range(lower_range..upper_range);
for _ in 0..10 {
if end.gcd(candidate) == 1 {
return candidate;
}
candidate = rng.gen_range(lower_range..upper_range);
candidate = rng.random_range(lower_range..upper_range);
}
end - 1
+4 -5
View File
@@ -25,8 +25,6 @@ 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 {
@@ -71,8 +69,6 @@ impl Scanner {
/// 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>`
/// 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
@@ -197,7 +193,10 @@ impl Scanner {
}
}
Ok(socket)
Err(io::Error::new(
io::ErrorKind::Other,
format!("UDP scan timed-out for all tries on socket {}", socket),
))
}
/// Performs the connection to the socket with timeout
+1 -1
View File
@@ -30,7 +30,7 @@ impl<'s> SocketIterator<'s> {
}
#[allow(clippy::doc_link_with_quotes)]
impl<'s> Iterator for SocketIterator<'s> {
impl Iterator for SocketIterator<'_> {
type Item = SocketAddr;
/// Returns a socket based on the combination of one of the provided
+60 -11
View File
@@ -108,17 +108,21 @@ pub fn init_scripts(scripts: &ScriptsRequired) -> Result<Vec<ScriptFile>> {
scripts_to_run.push(default_script);
}
ScriptsRequired::Custom => {
let scripts_dir_base =
dirs::home_dir().ok_or_else(|| anyhow!("Could not infer scripts path."))?;
let script_paths = find_scripts(scripts_dir_base)?;
let script_config = ScriptConfig::read_config()?;
debug!("Script config \n{:?}", script_config);
let script_dir_base = if let Some(config_directory) = &script_config.directory {
PathBuf::from(config_directory)
} else {
dirs::home_dir().ok_or_else(|| anyhow!("Could not infer scripts path."))?
};
let script_paths = find_scripts(script_dir_base)?;
debug!("Scripts paths \n{:?}", script_paths);
let parsed_scripts = parse_scripts(script_paths);
debug!("Scripts parsed \n{:?}", parsed_scripts);
let script_config = ScriptConfig::read_config()?;
debug!("Script config \n{:?}", script_config);
// Only Scripts that contain all the tags found in ScriptConfig will be selected.
if let Some(config_hashset) = script_config.tags {
for script in parsed_scripts {
@@ -281,7 +285,7 @@ fn execute_script(script: &str) -> Result<String> {
};
match Command::new(cmd)
.args(&[arg, script])
.args([arg, script])
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.output()
@@ -316,8 +320,7 @@ fn execute_script(script: &str) -> Result<String> {
}
}
pub fn find_scripts(mut path: PathBuf) -> Result<Vec<PathBuf>> {
path.push(".rustscan_scripts");
pub fn find_scripts(path: PathBuf) -> Result<Vec<PathBuf>> {
if path.is_dir() {
debug!("Scripts folder found {}", &path.display());
let mut files_vec: Vec<PathBuf> = Vec::new();
@@ -382,6 +385,7 @@ pub struct ScriptConfig {
pub tags: Option<Vec<String>>,
pub ports: Option<Vec<String>>,
pub developer: Option<Vec<String>>,
pub directory: Option<String>,
}
#[cfg(not(tarpaulin_include))]
@@ -400,7 +404,7 @@ impl ScriptConfig {
#[cfg(test)]
mod tests {
use super::{find_scripts, parse_scripts, Script, ScriptFile};
use super::*;
// Function for testing only, it inserts static values into ip and open_ports
// Doesn't use impl in case it's implemented in the super module at some point
@@ -418,7 +422,7 @@ mod tests {
#[test]
fn find_and_parse_scripts() {
let scripts = find_scripts("fixtures/".into()).unwrap();
let scripts = find_scripts("fixtures/.rustscan_scripts".into()).unwrap();
let scripts = parse_scripts(scripts);
assert_eq!(scripts.len(), 4);
}
@@ -515,4 +519,49 @@ mod tests {
// output has a newline at the end by default, .trim() trims it
assert_eq!(output.trim(), "Total args passed to fixtures/.rustscan_scripts/test_script.pl : 2\nArg # 1 : 127.0.0.1\nArg # 2 : 80,8080");
}
#[test]
fn test_custom_directory_config() {
// Create test config
let config_str = r#"
tags = ["core_approved", "example"]
directory = "fixtures/.rustscan_scripts"
"#;
let config: ScriptConfig = toml::from_str(config_str).unwrap();
assert_eq!(
config.directory,
Some("fixtures/.rustscan_scripts".to_string())
);
// Test that the directory is actually used
let script_dir_base = PathBuf::from(config.directory.unwrap());
let scripts = find_scripts(script_dir_base).unwrap();
// Verify we found the test script
assert!(scripts.iter().any(|p| p
.file_name()
.and_then(|f| f.to_str())
.map(|s| s == "test_script.txt")
.unwrap_or(false)));
}
#[test]
fn test_default_directory_fallback() {
let config_str = r#"
tags = ["core_approved", "example"]
"#;
let config: ScriptConfig = toml::from_str(config_str).unwrap();
assert_eq!(config.directory, None);
// Test fallback to home directory
let script_dir_base = if let Some(config_directory) = &config.directory {
PathBuf::from(config_directory)
} else {
dirs::home_dir().unwrap()
};
assert_eq!(script_dir_base, dirs::home_dir().unwrap());
}
}
+2 -2
View File
@@ -68,7 +68,7 @@ macro_rules! output {
macro_rules! funny_opening {
// prints a funny quote / opening
() => {
use rand::seq::SliceRandom;
use rand::seq::IndexedRandom;
let quotes = vec![
"Nmap? More like slowmap.🐢",
"🌍HACK THE PLANET🌍",
@@ -98,7 +98,7 @@ macro_rules! funny_opening {
"TreadStone was here 🚀",
"With RustScan, I scan ports so fast, even my firewall gets whiplash 💨",
];
let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap();
let random_quote = quotes.choose(&mut rand::rng()).unwrap();
println!("{}\n", random_quote);
};