Compare commits

..
Author SHA1 Message Date
Autumn Skerritt 70241a292e fix(config): let command-line options override the config file
`Opts::merge` copied every value present in the config file over the
parsed command line, so e.g. `-r 8600-8650` was silently replaced by the
config's `range` and the requested ports were never scanned (`-p` only
survived because `PortStrategy::pick` prefers `ports` over `range`).
The same happened to `-a`, `-b`, `-t`, `--tries`, `-g`, `--scripts`,
`--scan-order`, the trailing nmap command and every other merged option.
This contradicts the documented behaviour: "Any arguments passed
directly to the binary will override the default provided in this
config file".

`Opts::parse()` cannot tell a value typed by the user from a clap
default, so `Opts::read()` now parses through `ArgMatches` and records
the ids of the arguments whose `value_source` is `CommandLine` in a new
skipped `cli_args` field. `merge_required`/`merge_optional` leave those
fields alone; options not given on the command line are still filled in
from the config file. `--top` and `--no-config` behave as before.

Add pure parsing tests (no sockets) covering both directions.

Fixes #722
2026-10-01 17:04:48 +00:00
Syed Ghufran HassanandAutumn Skerritt cfb8645901 Refactor SocketIterator for allocation-free performance (#945)
* Refactor SocketIterator for allocation-free performance

Refactor SocketIterator to eliminate boxing and improve memory efficiency. Update documentation and tests to reflect changes.

* fix(socket_iterator): fix clone test off-by-one and skip private doc example

- clone_resumes_independently expected None after 3 of 4 sockets; assert
  the exact remaining sequence for both the original and the clone.
- The struct doc example referenced a placeholder crate and a private
  module, so it could never compile as a doctest; mark it ignore.
- Add a test for empty IP/port inputs covering size_hint and the
  FusedIterator promise.

---------

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 22:03:40 +09:00
ReacherandAutumn Skerritt d7cdad29c6 refactor(main): avoid per-IP script list clones and safer ulimit fallback (#905)
Small cleanups in main.rs:

- Iterate over the parsed scripts by reference and clone only the script being run, instead of cloning the whole list for every IP.
- Use `or_default()` for the per-IP port map.
- If the soft file-descriptor limit does not fit in a `usize` (32-bit targets), fall back to `DEFAULT_FILE_DESCRIPTORS_LIMIT` instead of `usize::MAX`, which bypassed all batch-size adjustments.
- Fix a typo.

The effective-batch-size warning fix originally proposed here landed via #921.

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 20:24:16 +09:00
dependabot[bot] ea9e6f0736 build(deps): bump toml from 1.1.5+spec-1.1.0 to 1.1.6+spec-1.1.0 (#931)
Bumps [toml](https://github.com/toml-rs/toml) from 1.1.5+spec-1.1.0 to 1.1.6+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v1.1.5...toml-v1.1.6)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.1.6+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 20:23:25 +09:00
dependabot[bot] 2c854eb551 build(deps): bump clap from 4.6.2 to 4.6.7 (#934)
Bumps [clap](https://github.com/clap-rs/clap) from 4.6.2 to 4.6.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.2...clap_complete-v4.6.7)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 20:23:10 +09:00
dependabot[bot] 1c3fd8c378 build(deps): bump rand from 0.10.2 to 0.10.3 (#936)
Bumps [rand](https://github.com/rust-random/rand) from 0.10.2 to 0.10.3.
- [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.10.2...0.10.3)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 20:22:54 +09:00
onsaliandAutumn Skerritt 4e64b5f00a feat: optional interval between scanned ports (#713)
New opt-in `--interval <MS>` option (also `interval` in the config file): after a port has been scanned on every address, wait that many milliseconds before scanning the next port, for slow, low-noise scans. Within a port `--batch-size` still limits concurrency; with the default of 0 scanning behaviour is unchanged.

Library: `Scanner::with_interval(Duration)` is added; `Scanner::new()` is unchanged.

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 20:19:49 +09:00
fabianbeesandAutumn Skerritt faa21b7d14 feat: option to also list closed ports (#736)
New `--closed` option (also `closed = true` in the config file) that additionally lists TCP ports that actively refused the connection: printed live as `Closed ip:port` and summarised under `closed ports:` at the end (also in greppable mode). Scripts are never run against closed ports, and without `--closed` scanning behaviour is unchanged.

Library: `Scanner::with_closed_ports()` and `Scanner::run_with_status() -> Vec<PortStatus>` are added; `Scanner::new()` and `run()` are unchanged.

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 20:14:48 +09:00
satyanand choudharyandAutumn Skerritt bf74950995 feat: support comma-separated multi-range port input (#852)
`-r/--range` now accepts several comma-separated ranges, e.g. `-r 1-100,200-300,5000-5100`. Overlapping/adjacent ranges are merged; random order uses the existing LCG walk extended over the merged ranges (every port visited exactly once), serial order follows the input order and skips duplicates. Reversed ranges (`300-200`) are now rejected instead of underflowing.

`range` in the config file keeps accepting the old `{ start = 1, end = 1000 }` table and additionally accepts `"1-500,1000-2500"` or `[[1, 500], [1000, 2500]]`.

Library API: `input::PortRange { start, end }` is replaced by `input::PortRanges(Vec<(u16, u16)>)`; `PortStrategy::pick` keeps its signature otherwise.

Fixes #835.

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 20:09:16 +09:00
Niklas Saari c027ebc7da test(address): cover CIDR notation with host bits set (#899)
Add regression tests for CIDR inputs whose host bits are set (e.g. `192.168.1.13/29` -> 192.168.1.8–15, `192.168.1.5/23`, `10.0.0.7/30`). The parsing itself was already fixed on master by 3bdac9c (IpInet accepts host bits), so the proposed normalize_cidr helper was dropped as unreachable.
2026-10-01 19:59:17 +09:00
DanDrewManand0xxreacher 183eafd22e fix: honor configured batch size on Windows and reject unsupported --ulimit (#921)
On native Windows, use the configured `-b/--batch-size` instead of a hard-coded 3000, and report the effective batch size (not the raw CLI value) in the "no open ports" hint on all platforms. `--ulimit` is hidden from Windows help and rejected on Windows (exit code 2), including when it comes from the config file. Unix batch-size/ulimit inference is unchanged.

Effective-batch-size reporting fix originally identified by @0xxreacher in #905.

Co-authored-by: 0xxreacher <[email protected]>
2026-10-01 19:57:28 +09:00
ChrisJr404 e497adf530 fix(config): use canonical XDG path for default config (#904)
Look for the config file at the XDG-style `$XDG_CONFIG_HOME/rustscan/config.toml` first (via `dirs::config_dir()`, so the platform equivalent on macOS/Windows), then fall back to the previous `$XDG_CONFIG_HOME/.rustscan.toml` and `~/.rustscan.toml` locations so existing configs keep working.

Fixes #741.
2026-10-01 19:55:08 +09:00
Chase Dustin 9ef667caed perf(scanner): precompute UDP payload lookup (#886)
Build a port -> payload lookup once per UDP scan instead of cloning the whole nmap payload BTreeMap into every scanned socket and scanning it linearly. Payload selection keeps the previous "last match wins" semantics.

Adds a socket-free micro-benchmark (benches/benchmark_helpers.rs) and lookup tests (tests/udp_payload_lookup.rs).
2026-10-01 19:54:38 +09:00
Andrew Barnes 002f736073 fix: keep library scans quiet by default (#916)
`Scanner` no longer prints each open socket to stdout by default, so library users can embed it and use the returned sockets without unsolicited output. The CLI opts in via the new `Scanner::with_open_port_output()` builder. The UDP socket bind error is now logged at debug level instead of printed.

Fixes #706.
2026-10-01 19:52:04 +09:00
kyd 1c2a982320 fix(build): decode literal text in UDP payload probes (#938)
Rewrite the nmap-payloads decoder in build.rs to follow Nmap's C-style escape format: `\xNN` and single-character escapes are decoded, all other characters are kept literally, and multi-line quoted segments are concatenated. Literal protocol text (e.g. the SNMP `public` community string, NetBIOS names, SSDP headers) was previously dropped, producing malformed probes. Also strip CR line endings when reading nmap-payloads.

Adds regression tests pinning the SNMP (161) and SSDP (1900) probe bytes.

Fixes #933.
2026-10-01 19:48:04 +09:00
jk 9644b3a2cd fix: exit quietly on closed stdout and survive missing SystemRoot (#941)
Route all user-facing stdout writes through tui::println_safe, which exits 0 on BrokenPipe (e.g. `rustscan -g ... | head`) instead of panicking (fatal under panic = "abort"). On Windows, skip hickory-resolver's system configuration and hosts file when SystemRoot is unset, falling back to the existing cloudflare_tls resolver.

Fixes #939, fixes #940.
2026-10-01 19:43:53 +09:00
herbenderblerandAutumn Skerritt a21bbb31bb ci: run the test suite on arm64 Linux runners (#868)
Add the GitHub-hosted ubuntu-24.04-arm runner to the test matrix so the suite runs natively on arm64 Linux (macos-latest already covers arm64 macOS; build.yml already cross-builds armv7/aarch64 Linux binaries), and document ARM support in docs/arm-support.md.

Fixes #683.

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 19:42:49 +09:00
Brad CowieandAutumn Skerritt 8b143f8ad7 ci: upload build artifacts to GitHub releases (#858)
Build release archives with houseabsolute/actions-rust-release and publish them (plus the .deb and checksums) to the GitHub release when a tag is pushed. CD now runs on pushes to master and tags only.

Also: read-only default workflow token (publish job gets contents: write), third-party actions pinned to release SHAs, fail on unmatched release assets, and the disabled trigger-less dependabot auto-merge workflow is removed.

Fixes #700, fixes #801.

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 19:41:56 +09:00
Sujyot KambleandAutumn Skerritt 111316f446 ci: replace deprecated actions-rs toolchain in test workflow (#853)
Replace the deprecated actions-rs/toolchain action with actions-rust-lang/setup-rust-toolchain (pinned to v1.17.0, clippy + rustfmt components, built-in cache and default RUSTFLAGS disabled) and restrict the workflow token to contents: read.

Co-authored-by: Autumn Skerritt <[email protected]>
2026-10-01 19:32:38 +09:00
Autumn (Bee) 8be97b841d test: clean obsolete executable fixtures and CI hooks (#943) 2026-10-01 18:05:37 +09:00
Autumn (Bee) 7446a42e56 test: remove network scans and external script execution (#942) 2026-10-01 17:44:10 +09:00
Autumn (Bee) e9ba3fa547 Merge pull request #935 from rezamqds/master
Fixes a typo in the Scriptfile parser comment (Sriptfile → Scriptfile) and improves the wording for clarity.
2026-09-21 19:26:33 +09:00
rezamqds 9a45d26e99 Fix typo in Scriptfile comment
Fix typo in comment about Scriptfile parser
2026-09-20 17:28:35 +03:30
rezamqds 6cb99747e1 Fix typo in Scriptfile comment
Clarify comment on scriptfile parser behavior
2026-09-20 17:28:31 +03:30
rezamqds bc80fbbad4 Fix typo in Scriptfile comment
Fix comment for clarity in test_script.py
2026-09-20 17:27:10 +03:30
rezamqds 3168580b86 Fix typo in Scriptfile comment
Fix comment for clarity in test_script.pl
2026-09-20 17:24:58 +03:30
Autumn (Bee) 2e78c22644 Merge pull request #930 from bee-san/fix/dependabot-clippy
chore: merge Dependabot updates and restore stable Clippy checks
2026-09-16 15:31:58 +09:00
Autumn Skerritt 996f818b5e Merge remote-tracking branch 'origin/dependabot/cargo/log-0.4.34' into fix/dependabot-clippy 2026-09-16 06:13:58 +00:00
Autumn Skerritt 90c5c1766b Merge commit '55c9f2a1c87b63f23363f6626954b871bbf4ab8e' into fix/dependabot-clippy 2026-09-16 06:13:37 +00:00
Autumn Skerritt f570d9ff8e Merge commit 'ce880ebe8c83be476d217f96ca26c2f90e6261c3' into fix/dependabot-clippy 2026-09-16 06:13:36 +00:00
Autumn Skerritt 42a3c87c83 Merge commit '64f60a97eddd7a0445be732175f272960ae1bd08' into fix/dependabot-clippy
# Conflicts:
#	Cargo.toml
2026-09-16 06:13:36 +00:00
Autumn Skerritt 1e93f44d8b Merge commit 'a084c1b3b9d6831074ff1929316a14a510e32430' into fix/dependabot-clippy 2026-09-16 06:12:26 +00:00
Autumn Skerritt bf290d7c7f Merge commit 'c668636a619ca96906be7d0b2aaf4ed0356560e3' into fix/dependabot-clippy 2026-09-16 06:12:26 +00:00
Autumn Skerritt 160dc7a3a2 Merge commit '730f709fd631955582993bc03d2aab5f580aa63c' into fix/dependabot-clippy 2026-09-16 06:12:26 +00:00
Autumn Skerritt 2d52f239ca Merge commit '827acd9cb00255662954cc36b5a90f6dbe5ba54f' into fix/dependabot-clippy 2026-09-16 06:12:26 +00:00
Autumn Skerritt 5900974fda Merge commit 'f9bc5abf212d85154d91b973f4761ac62950db11' into fix/dependabot-clippy 2026-09-16 06:12:26 +00:00
Autumn Skerritt d0d388c813 Merge commit '82c1a9373adfce5345c1745f90c9984656fbadc3' into fix/dependabot-clippy 2026-09-16 06:12:26 +00:00
Autumn Skerritt 5492dd729c fix: satisfy current stable Clippy without suppressing checks 2026-09-16 06:12:25 +00:00
Autumn Skerritt b84f1fa046 Merge remote-tracking branch 'origin/master' into fix/dependabot-clippy 2026-09-16 06:05:38 +00:00
dependabot[bot] 3d4db2b6a1 build(deps): bump log from 0.4.32 to 0.4.34
Bumps [log](https://github.com/rust-lang/log) from 0.4.32 to 0.4.34.
- [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.32...0.4.34)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-09-16 06:05:03 +00:00
dependabot[bot] 1967598d96 build(deps): bump dirs from 6.0.0 to 7.0.0 (#924)
Bumps dirs from 6.0.0 to 7.0.0.

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-16 15:02:40 +09:00
dependabot[bot] 94be5711d9 build(deps): bump toml from 1.1.2+spec-1.1.0 to 1.1.5+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 1.1.2+spec-1.1.0 to 1.1.5+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v1.1.2...toml-v1.1.5)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.1.5+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-09-07 09:03:00 +00:00
dependabot[bot] 55c9f2a1c8 build(deps): bump clap from 4.6.1 to 4.6.2
Bumps [clap](https://github.com/clap-rs/clap) from 4.6.1 to 4.6.2.
- [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.6.1...clap_complete-v4.6.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-07-16 09:03:00 +00:00
dependabot[bot] ce880ebe8c build(deps): bump rand from 0.10.1 to 0.10.2
Bumps [rand](https://github.com/rust-random/rand) from 0.10.1 to 0.10.2.
- [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.10.1...0.10.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-07-02 09:02:49 +00:00
dependabot[bot] 64f60a97ed build(deps): bump cidr-utils from 0.6.2 to 0.7.1
Bumps [cidr-utils](https://github.com/magiclen/cidr-utils) from 0.6.2 to 0.7.1.
- [Commits](https://github.com/magiclen/cidr-utils/compare/v0.6.2...v0.7.1)

---
updated-dependencies:
- dependency-name: cidr-utils
  dependency-version: 0.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-06-29 09:03:10 +00:00
dependabot[bot] a084c1b3b9 build(deps): bump env_logger from 0.11.10 to 0.11.11
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.10 to 0.11.11.
- [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.10...v0.11.11)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-06-26 09:02:48 +00:00
dependabot[bot] c668636a61 build(deps): bump actions/checkout from 6 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-06-19 09:03:11 +00:00
dependabot[bot] 730f709fd6 build(deps): bump itertools from 0.14.0 to 0.15.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.14.0 to 0.15.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.14.0...v0.15.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-06-17 09:03:14 +00:00
Roger G. Coram e9dadb4a30 Merge pull request #902 from bee-san/dependabot/github_actions/actions/checkout-6
build(deps): bump actions/checkout from 4 to 6
2026-06-10 11:33:31 +01:00
Roger G. Coram 036297fbb9 Merge pull request #898 from bee-san/dependabot/cargo/clap-4.6.1
build(deps): bump clap from 4.6.0 to 4.6.1
2026-06-10 11:32:43 +01:00
Roger G. Coram e7db81a478 Merge pull request #908 from bee-san/dependabot/cargo/log-0.4.32
build(deps): bump log from 0.4.29 to 0.4.32
2026-06-10 11:32:09 +01:00
Roger G. Coram 7c7018530b Merge pull request #896 from bee-san/dependabot/cargo/toml-1.1.2spec-1.1.0
build(deps): bump toml from 1.1.0+spec-1.1.0 to 1.1.2+spec-1.1.0
2026-06-10 11:31:47 +01:00
Roger G. Coram 3abd1abe68 Merge pull request #897 from bee-san/dependabot/cargo/rand-0.10.1
build(deps): bump rand from 0.10.0 to 0.10.1
2026-06-10 11:31:13 +01:00
dependabot[bot] a4099a4ec2 build(deps): bump log from 0.4.29 to 0.4.32
Bumps [log](https://github.com/rust-lang/log) from 0.4.29 to 0.4.32.
- [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.29...0.4.32)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-06-04 09:02:56 +00:00
dependabot[bot] 827acd9cb0 build(deps): bump actions/upload-artifact from 4 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-30 09:06:04 +00:00
dependabot[bot] e724d1953c build(deps): bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-30 09:06:00 +00:00
dependabot[bot] f9bc5abf21 build(deps): bump houseabsolute/actions-rust-cross from 0 to 1
Bumps [houseabsolute/actions-rust-cross](https://github.com/houseabsolute/actions-rust-cross) from 0 to 1.
- [Release notes](https://github.com/houseabsolute/actions-rust-cross/releases)
- [Changelog](https://github.com/houseabsolute/actions-rust-cross/blob/v1/Changes.md)
- [Commits](https://github.com/houseabsolute/actions-rust-cross/compare/v0...v1)

---
updated-dependencies:
- dependency-name: houseabsolute/actions-rust-cross
  dependency-version: '1'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-30 09:05:56 +00:00
dependabot[bot] 82c1a9373a build(deps): bump extractions/setup-just from 2 to 4
Bumps [extractions/setup-just](https://github.com/extractions/setup-just) from 2 to 4.
- [Release notes](https://github.com/extractions/setup-just/releases)
- [Commits](https://github.com/extractions/setup-just/compare/v2...v4)

---
updated-dependencies:
- dependency-name: extractions/setup-just
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-30 09:05:52 +00:00
dependabot[bot] 45ece51c63 build(deps): bump clap from 4.6.0 to 4.6.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.6.0 to 4.6.1.
- [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.6.0...clap_complete-v4.6.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-21 09:04:00 +00:00
dependabot[bot] e234127874 build(deps): bump rand from 0.10.0 to 0.10.1
Bumps [rand](https://github.com/rust-random/rand) from 0.10.0 to 0.10.1.
- [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.10.0...0.10.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-13 09:13:48 +00:00
dependabot[bot] 053a3eb0eb build(deps): bump toml from 1.1.0+spec-1.1.0 to 1.1.2+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 1.1.0+spec-1.1.0 to 1.1.2+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v1.1.0...toml-v1.1.2)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.1.2+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-02 09:03:34 +00:00
Roger G. Coram b03379890e Merge pull request #893 from bee-san/dependabot/cargo/env_logger-0.11.10
build(deps): bump env_logger from 0.11.9 to 0.11.10
2026-03-26 07:47:59 +00:00
Roger G. Coram 52ba449b70 Merge pull request #894 from bee-san/dependabot/cargo/toml-1.1.0spec-1.1.0
build(deps): bump toml from 1.0.6+spec-1.1.0 to 1.1.0+spec-1.1.0
2026-03-26 07:47:33 +00:00
dependabot[bot] 79768931e3 build(deps): bump toml from 1.0.6+spec-1.1.0 to 1.1.0+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 1.0.6+spec-1.1.0 to 1.1.0+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v1.0.6...toml-v1.1.0)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.1.0+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-24 09:03:49 +00:00
dependabot[bot] 3c3a69b06b build(deps): bump env_logger from 0.11.9 to 0.11.10
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.9 to 0.11.10.
- [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.9...v0.11.10)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-24 09:03:39 +00:00
Roger G. Coram 487acbe382 Merge pull request #888 from bee-san/dependabot/cargo/toml-1.0.6spec-1.1.0
build(deps): bump toml from 1.0.4+spec-1.1.0 to 1.0.6+spec-1.1.0
2026-03-14 10:17:00 +00:00
Roger G. Coram 8115b2d62e Merge pull request #889 from bee-san/dependabot/cargo/clap-4.6.0
build(deps): bump clap from 4.5.60 to 4.6.0
2026-03-14 10:16:24 +00:00
Roger G. Coram 99c3c1a650 Merge pull request #890 from bee-san/dependabot/cargo/once_cell-1.21.4
build(deps): bump once_cell from 1.21.3 to 1.21.4
2026-03-14 10:15:47 +00:00
dependabot[bot] f366ce741d build(deps): bump once_cell from 1.21.3 to 1.21.4
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.21.3 to 1.21.4.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.21.3...v1.21.4)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-13 09:03:56 +00:00
dependabot[bot] 6f388ae152 build(deps): bump clap from 4.5.60 to 4.6.0
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.60 to 4.6.0.
- [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.60...clap_complete-v4.6.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-13 09:03:46 +00:00
dependabot[bot] a11b624644 build(deps): bump toml from 1.0.4+spec-1.1.0 to 1.0.6+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 1.0.4+spec-1.1.0 to 1.0.6+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v1.0.4...toml-v1.0.6)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.0.6+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-09 09:05:12 +00:00
Roger G. Coram 85c080ee56 Merge pull request #887 from bee-san/dependabot/cargo/toml-1.0.4spec-1.1.0
build(deps): bump toml from 1.0.0+spec-1.1.0 to 1.0.4+spec-1.1.0
2026-03-07 11:00:36 +00:00
Roger G. Coram b367fbe48e Merge pull request #885 from bee-san/dependabot/cargo/clap-4.5.60
build(deps): bump clap from 4.5.58 to 4.5.60
2026-03-07 10:59:44 +00:00
dependabot[bot] 50aed26d29 build(deps): bump toml from 1.0.0+spec-1.1.0 to 1.0.4+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 1.0.0+spec-1.1.0 to 1.0.4+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v1.0.0...toml-v1.0.4)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.0.4+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-05 09:03:36 +00:00
dependabot[bot] 1ceb758acf build(deps): bump clap from 4.5.58 to 4.5.60
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.58 to 4.5.60.
- [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.58...clap_complete-v4.5.60)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-20 09:03:53 +00:00
Roger G. Coram 07aa04a94d Merge pull request #880 from bee-san/dependabot/cargo/clap-4.5.58
build(deps): bump clap from 4.5.57 to 4.5.58
2026-02-13 08:34:08 +00:00
dependabot[bot] 42d2f6128e build(deps): bump clap from 4.5.57 to 4.5.58
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.57 to 4.5.58.
- [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.57...clap_complete-v4.5.58)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-12 16:40:40 +00:00
Roger G. Coram f71ed28ff7 Merge pull request #879 from bee-san/dependabot/cargo/anstream-1.0.0
build(deps): bump anstream from 0.6.20 to 1.0.0
2026-02-12 16:39:41 +00:00
dependabot[bot] bb8aa7fa80 build(deps): bump anstream from 0.6.20 to 1.0.0
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.20 to 1.0.0.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.20...anstream-v1.0.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-12 16:30:24 +00:00
Roger G. Coram cd9852ae9d Merge pull request #878 from bee-san/dependabot/cargo/toml-1.0.0spec-1.1.0
build(deps): bump toml from 0.9.12+spec-1.1.0 to 1.0.0+spec-1.1.0
2026-02-12 16:28:44 +00:00
Roger G. Coram 6cd10727ee Merge pull request #877 from bee-san/dependabot/cargo/env_logger-0.11.9
build(deps): bump env_logger from 0.11.8 to 0.11.9
2026-02-12 16:27:39 +00:00
dependabot[bot] 034cbccf7e build(deps): bump toml from 0.9.12+spec-1.1.0 to 1.0.0+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.12+spec-1.1.0 to 1.0.0+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.12...toml-v1.0.0)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.0.0+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-12 09:03:59 +00:00
dependabot[bot] 6bae2a1aa2 build(deps): bump env_logger from 0.11.8 to 0.11.9
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.8 to 0.11.9.
- [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.8...v0.11.9)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-12 09:03:47 +00:00
Roger G. Coram 36e303e6ad Merge pull request #876 from bee-san/dependabot/cargo/toml-0.9.12spec-1.1.0
build(deps): bump toml from 0.9.7 to 0.9.12+spec-1.1.0
2026-02-12 08:22:13 +00:00
dependabot[bot] 3c2c869ba9 build(deps): bump toml from 0.9.7 to 0.9.12+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.7 to 0.9.12+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.7...toml-v0.9.12)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 0.9.12+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-11 09:03:49 +00:00
Roger G. Coramanddependabot[bot] 7f2fc87aa3 build(deps): bump rand from 0.9.2 to 0.10.0 (#875)
* build(deps): bump rand from 0.9.2 to 0.10.0

Bumps [rand](https://github.com/rust-random/rand) from 0.9.2 to 0.10.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/rand_core-0.9.2...0.10.0)

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

Signed-off-by: dependabot[bot] <[email protected]>

* fix: change rand import

as per recent changelog.

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 10:58:40 +00:00
Roger G. Coram 6440aed8a7 Merge pull request #872 from bee-san/dependabot/cargo/criterion-0.8.2
build(deps): bump criterion from 0.7.0 to 0.8.2
2026-02-10 10:16:05 +00:00
dependabot[bot] 51efbca7aa build(deps): bump criterion from 0.7.0 to 0.8.2
Bumps [criterion](https://github.com/criterion-rs/criterion.rs) from 0.7.0 to 0.8.2.
- [Release notes](https://github.com/criterion-rs/criterion.rs/releases)
- [Changelog](https://github.com/criterion-rs/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/criterion-rs/criterion.rs/compare/criterion-plot-v0.7.0...criterion-v0.8.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-10 10:01:16 +00:00
Roger G. Coram c78975fa87 Merge pull request #871 from bee-san/dependabot/cargo/clap-4.5.57
build(deps): bump clap from 4.5.48 to 4.5.57
2026-02-10 09:59:42 +00:00
dependabot[bot] 3a7230127f build(deps): bump clap from 4.5.48 to 4.5.57
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.48 to 4.5.57.
- [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.48...clap_complete-v4.5.57)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-10 09:53:36 +00:00
Roger G. Coram 6dbf9f51cb Merge pull request #870 from bee-san/dependabot/cargo/bytes-1.11.1
build(deps): bump bytes from 1.10.0 to 1.11.1
2026-02-10 09:46:24 +00:00
dependabot[bot] c7a265e270 build(deps): bump bytes from 1.10.0 to 1.11.1
Bumps [bytes](https://github.com/tokio-rs/bytes) from 1.10.0 to 1.11.1.
- [Release notes](https://github.com/tokio-rs/bytes/releases)
- [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/bytes/compare/v1.10.0...v1.11.1)

---
updated-dependencies:
- dependency-name: bytes
  dependency-version: 1.11.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-10 09:40:51 +00:00
Roger G. Coram e3d31b3957 Merge pull request #869 from bee-san/dependabot/cargo/rlimit-0.11.0
build(deps): bump rlimit from 0.10.2 to 0.11.0
2026-02-10 09:39:22 +00:00
dependabot[bot] b0965dc946 build(deps): bump rlimit from 0.10.2 to 0.11.0
Bumps [rlimit](https://github.com/Nugine/rlimit) from 0.10.2 to 0.11.0.
- [Changelog](https://github.com/Nugine/rlimit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Nugine/rlimit/compare/v0.10.2...v0.11.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-10 09:22:27 +00:00
Roger G. Coram 2c66b4203a Merge pull request #865 from bee-san/dependabot/cargo/colored-3.1.1
build(deps): bump colored from 3.0.0 to 3.1.1
2026-02-10 09:21:26 +00:00
dependabot[bot] c8596c7679 build(deps): bump colored from 3.0.0 to 3.1.1
Bumps [colored](https://github.com/mackwic/colored) from 3.0.0 to 3.1.1.
- [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/v3.0.0...v3.1.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-10 09:16:32 +00:00
Roger G. Coram aeef44c6e0 test: correct Clippy warnings (#874)
[`unnecessary_unwrap`](https://rust-lang.github.io/rust-clippy/rust-1.93.0/index.html#unnecessary_unwrap)
2026-02-10 08:37:35 +00:00
Roger G. Coram b228abfaa9 Merge pull request #857 from bee-san/dependabot/cargo/cidr-utils-0.6.2
build(deps): bump cidr-utils from 0.6.1 to 0.6.2
2026-02-10 08:18:19 +00:00
Roger G. Coram 7fbd96be7c Merge pull request #850 from bee-san/dependabot/cargo/log-0.4.29
build(deps): bump log from 0.4.28 to 0.4.29
2026-02-10 08:14:51 +00:00
dependabot[bot] 4a7ca7c00b build(deps): bump cidr-utils from 0.6.1 to 0.6.2
Bumps [cidr-utils](https://github.com/magiclen/cidr-utils) from 0.6.1 to 0.6.2.
- [Commits](https://github.com/magiclen/cidr-utils/compare/v0.6.1...v0.6.2)

---
updated-dependencies:
- dependency-name: cidr-utils
  dependency-version: 0.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-26 09:03:16 +00:00
dependabot[bot] 60a5ae0102 build(deps): bump log from 0.4.28 to 0.4.29
Bumps [log](https://github.com/rust-lang/log) from 0.4.28 to 0.4.29.
- [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.28...0.4.29)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-03 09:05:12 +00:00
bee 1b74455a35 fix(ci) 2025-11-27 11:11:39 +09:00
Jakub Kołodziej 00d1003afa Change batch_size and ulimit type (#811)
* Change batch_size and ulimit type from u16 and u64 to usize

* add error handling for overflow

* remove unnecessary conversions `clippy`

* fix missed

* chore: fix CI (unrelated)
2025-11-27 11:04:53 +09:00
4c056a9039 [Enhancement] Read config file from platform-specific config folder (#839)
* read config file from platform-specific config folder

* Update src/input.rs

Co-authored-by: Copilot <[email protected]>

---------

Co-authored-by: Autumn (Bee) <[email protected]>
Co-authored-by: Copilot <[email protected]>
2025-11-27 10:50:14 +09:00
defigli a40c4ed17f feat: implement config file support for no_banner (fixes #845) (#847) 2025-11-27 10:49:23 +09:00
bee 59c6f10fad fix ci 2025-11-27 10:48:45 +09:00
bee ec2d714e39 fix tests 2025-11-27 10:46:39 +09:00
Ben Woods c19726c74f README.md: fix arch install, install guide link (#816)
The example installation for Arch Linux previously showed installation via yay, which is an unofficial package manager only required if installing from the AUR. Since rustscan is in the official extra package repository, change the instructions to use pacman.

While here, also fix link to full install guide.
2025-11-24 22:46:01 +09:00
Autumn (Bee) 2600036621 break dependabot 2025-09-24 20:02:10 +09:00
github-actions[bot] 9596712a19 Merge pull request #833 from bee-san/dependabot/cargo/clap-4.5.48
build(deps): bump clap from 4.5.47 to 4.5.48
2025-09-22 09:16:08 +00:00
dependabot[bot] fe98a3b6ef build(deps): bump clap from 4.5.47 to 4.5.48
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.47 to 4.5.48.
- [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.47...clap_complete-v4.5.48)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-09-22 09:15:55 +00:00
github-actions[bot] f4411e551a Merge pull request #832 from bee-san/dependabot/cargo/toml-0.9.7
build(deps): bump toml from 0.9.6 to 0.9.7
2025-09-19 09:04:20 +00:00
dependabot[bot] 0640d94f53 build(deps): bump toml from 0.9.6 to 0.9.7
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.6 to 0.9.7.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.6...toml-v0.9.7)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-09-19 09:04:09 +00:00
github-actions[bot] d3508f8192 Merge pull request #830 from bee-san/dependabot/cargo/toml-0.9.6
build(deps): bump toml from 0.9.5 to 0.9.6
2025-09-16 09:05:32 +00:00
dependabot[bot] aacde90cf1 build(deps): bump toml from 0.9.5 to 0.9.6
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.5 to 0.9.6.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.5...toml-v0.9.6)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-09-16 09:05:17 +00:00
github-actions[bot] 81a367e40b Merge pull request #828 from bee-san/dependabot/cargo/log-0.4.28
build(deps): bump log from 0.4.27 to 0.4.28
2025-09-04 12:59:57 +00:00
dependabot[bot] cc418f08b4 build(deps): bump log from 0.4.27 to 0.4.28
Bumps [log](https://github.com/rust-lang/log) from 0.4.27 to 0.4.28.
- [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.27...0.4.28)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-09-04 12:59:46 +00:00
github-actions[bot] 6781108db1 Merge pull request #827 from bee-san/dependabot/cargo/clap-4.5.47
build(deps): bump clap from 4.5.46 to 4.5.47
2025-09-03 23:58:30 +00:00
dependabot[bot] ea8015747f build(deps): bump clap from 4.5.46 to 4.5.47
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.46 to 4.5.47.
- [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.46...clap_complete-v4.5.47)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-09-03 23:58:18 +00:00
github-actions[bot] c09fb7bcfd Merge pull request #826 from bee-san/dependabot/cargo/clap-4.5.46
build(deps): bump clap from 4.5.45 to 4.5.46
2025-08-27 18:22:09 +00:00
dependabot[bot] 6b96e84878 build(deps): bump clap from 4.5.45 to 4.5.46
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.45 to 4.5.46.
- [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.45...clap_complete-v4.5.46)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-27 18:21:55 +00:00
github-actions[bot] 6f8b7f42f4 Merge pull request #823 from bee-san/dependabot/cargo/async-std-1.13.2
build(deps): bump async-std from 1.13.1 to 1.13.2
2025-08-15 09:19:32 +00:00
dependabot[bot] 24ff49622c build(deps): bump async-std from 1.13.1 to 1.13.2
Bumps [async-std](https://github.com/async-rs/async-std) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/async-rs/async-std/releases)
- [Changelog](https://github.com/async-rs/async-std/blob/main/CHANGELOG.md)
- [Commits](https://github.com/async-rs/async-std/compare/v1.13.1...v1.13.2)

---
updated-dependencies:
- dependency-name: async-std
  dependency-version: 1.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-15 09:19:22 +00:00
github-actions[bot] 87399a1583 Merge pull request #821 from bee-san/dependabot/cargo/clap-4.5.45
build(deps): bump clap from 4.5.44 to 4.5.45
2025-08-13 09:50:37 +00:00
dependabot[bot] 578457f6bd build(deps): bump clap from 4.5.44 to 4.5.45
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.44 to 4.5.45.
- [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.44...clap_complete-v4.5.45)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-13 09:50:26 +00:00
github-actions[bot] e315f82d03 Merge pull request #820 from bee-san/dependabot/cargo/clap-4.5.44
build(deps): bump clap from 4.5.43 to 4.5.44
2025-08-12 15:16:50 +00:00
dependabot[bot] 63c9fdc05a build(deps): bump clap from 4.5.43 to 4.5.44
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.43 to 4.5.44.
- [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.43...clap_complete-v4.5.44)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-12 15:16:40 +00:00
github-actions[bot] 6a1dd069b1 Merge pull request #819 from bee-san/dependabot/cargo/clap-4.5.43
build(deps): bump clap from 4.5.41 to 4.5.43
2025-08-07 09:07:30 +00:00
dependabot[bot] c89fad6c58 build(deps): bump clap from 4.5.41 to 4.5.43
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.41 to 4.5.43.
- [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.41...clap_complete-v4.5.43)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-07 09:07:21 +00:00
github-actions[bot] ac83fe7da0 Merge pull request #818 from bee-san/dependabot/cargo/anstream-0.6.20
build(deps): bump anstream from 0.6.19 to 0.6.20
2025-08-06 09:28:03 +00:00
dependabot[bot] 3a06c22b43 build(deps): bump anstream from 0.6.19 to 0.6.20
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.19 to 0.6.20.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.19...anstream-v0.6.20)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-06 09:27:54 +00:00
github-actions[bot] 9524b3503d Merge pull request #817 from bee-san/dependabot/cargo/toml-0.9.5
build(deps): bump toml from 0.9.4 to 0.9.5
2025-08-05 16:19:40 +00:00
dependabot[bot] eb11342a66 build(deps): bump toml from 0.9.4 to 0.9.5
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.4 to 0.9.5.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.4...toml-v0.9.5)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-05 16:19:26 +00:00
github-actions[bot] 7e8230f762 Merge pull request #814 from bee-san/dependabot/cargo/toml-0.9.4
build(deps): bump toml from 0.9.3 to 0.9.4
2025-07-30 09:37:43 +00:00
dependabot[bot] d51fbb7411 build(deps): bump toml from 0.9.3 to 0.9.4
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.3 to 0.9.4.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.3...toml-v0.9.4)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-30 09:37:25 +00:00
github-actions[bot] 56a234b41f Merge pull request #813 from bee-san/dependabot/cargo/criterion-0.7.0
build(deps): bump criterion from 0.6.0 to 0.7.0
2025-07-29 09:09:25 +00:00
dependabot[bot] 59b645aa0e build(deps): bump criterion from 0.6.0 to 0.7.0
Bumps [criterion](https://github.com/bheisler/criterion.rs) from 0.6.0 to 0.7.0.
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.6.0...0.7.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-29 09:09:12 +00:00
github-actions[bot] f5cbfef10f Merge pull request #812 from bee-san/dependabot/cargo/toml-0.9.3
build(deps): bump toml from 0.9.2 to 0.9.3
2025-07-29 09:09:02 +00:00
dependabot[bot] 0772c4948d build(deps): bump toml from 0.9.2 to 0.9.3
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.2 to 0.9.3.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.2...toml-v0.9.3)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-29 09:08:52 +00:00
github-actions[bot] 06e37f114e Merge pull request #808 from bee-san/dependabot/cargo/rand-0.9.2
build(deps): bump rand from 0.9.1 to 0.9.2
2025-07-21 10:22:19 +00:00
dependabot[bot] 18a71f2625 build(deps): bump rand from 0.9.1 to 0.9.2
Bumps [rand](https://github.com/rust-random/rand) from 0.9.1 to 0.9.2.
- [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/rand_core-0.9.1...rand_core-0.9.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-21 10:22:10 +00:00
github-actions[bot] 48f826e545 Merge pull request #806 from bee-san/dependabot/cargo/toml-0.9.2
build(deps): bump toml from 0.9.1 to 0.9.2
2025-07-14 11:40:37 +00:00
dependabot[bot] f1006e4b3c build(deps): bump toml from 0.9.1 to 0.9.2
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.1 to 0.9.2.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.1...toml-v0.9.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-14 11:40:26 +00:00
github-actions[bot] a7257acbbb Merge pull request #804 from bee-san/dependabot/cargo/toml-0.9.1
build(deps): bump toml from 0.9.0 to 0.9.1
2025-07-11 09:41:19 +00:00
dependabot[bot] 23724292e4 build(deps): bump toml from 0.9.0 to 0.9.1
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.0 to 0.9.1.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.0...toml-v0.9.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-11 09:41:08 +00:00
Melroy dsilva e1e5e7a020 (BUG)Typo in the tui.rs file and change in the contributing.Dockerfile (#797)
* change in the contributing.Docker File

* typo in the tui.rs file !
2025-07-11 15:55:57 +09:00
ggFROOK 2e85a6e31d change from HashMap to Vec (#792) 2025-07-11 15:54:25 +09:00
github-actions[bot] 9b43d1c6ce Merge pull request #803 from bee-san/dependabot/cargo/clap-4.5.41
build(deps): bump clap from 4.5.40 to 4.5.41
2025-07-10 09:30:17 +00:00
dependabot[bot] 7f7de101dc build(deps): bump clap from 4.5.40 to 4.5.41
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.40 to 4.5.41.
- [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.40...clap_complete-v4.5.41)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-10 09:30:06 +00:00
github-actions[bot] c353cf5fd8 Merge pull request #802 from bee-san/dependabot/cargo/toml-0.9.0
build(deps): bump toml from 0.8.23 to 0.9.0
2025-07-09 09:50:32 +00:00
dependabot[bot] 94b9c0e30a build(deps): bump toml from 0.8.23 to 0.9.0
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.23 to 0.9.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.23...toml-v0.9.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-07-09 09:50:20 +00:00
github-actions[bot] 042875afec Merge pull request #794 from bee-san/dependabot/cargo/clap-4.5.40
build(deps): bump clap from 4.5.39 to 4.5.40
2025-06-10 09:31:15 +00:00
dependabot[bot] edc90a4db3 build(deps): bump clap from 4.5.39 to 4.5.40
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.39 to 4.5.40.
- [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.39...clap_complete-v4.5.40)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-06-10 09:31:05 +00:00
github-actions[bot] 028554c18f Merge pull request #793 from bee-san/dependabot/cargo/toml-0.8.23
build(deps): bump toml from 0.8.22 to 0.8.23
2025-06-09 09:44:13 +00:00
dependabot[bot] f979926275 build(deps): bump toml from 0.8.22 to 0.8.23
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.22 to 0.8.23.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.22...toml-v0.8.23)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-06-09 09:44:03 +00:00
github-actions[bot] cf1eaf8e5c Merge pull request #790 from bee-san/dependabot/cargo/anstream-0.6.19
build(deps): bump anstream from 0.6.18 to 0.6.19
2025-06-05 09:33:59 +00:00
dependabot[bot] 618b8aa9a9 build(deps): bump anstream from 0.6.18 to 0.6.19
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.18 to 0.6.19.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.18...anstream-v0.6.19)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-06-05 09:33:46 +00:00
Roger G. Coram 0f23a671a5 Merge pull request #789 from zombiepigdragon/permissive-cidr
Allow CIDR addresses with non-zero host bits
2025-06-02 06:34:28 -07:00
Alex Rawson 3bdac9c9c7 allow CIDR addresses with non-zero host bits
fixes #777
2025-05-31 13:44:45 -05:00
Roger G. Coram 1085a723c4 Merge pull request #787 from mikhail5555/improve-excluded_nets-filter-performance
Improve excluded nets filter performance dractically
2025-05-31 09:37:48 -07:00
Mikhail Epifanov e1c8c254a1 fix all clippy errors 2025-05-31 15:07:13 +02:00
github-actions[bot] 05e5c50f49 Merge pull request #788 from bee-san/dependabot/cargo/clap-4.5.39
build(deps): bump clap from 4.5.38 to 4.5.39
2025-05-28 09:50:31 +00:00
dependabot[bot] f42f887da3 build(deps): bump clap from 4.5.38 to 4.5.39
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.38 to 4.5.39.
- [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.38...clap_complete-v4.5.39)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-05-28 09:50:20 +00:00
Mikhail Epifanov d5b7f4a024 use and a flat_map for the addresses 2025-05-23 14:04:53 +02:00
Mikhail Epifanov c5d54880fc switch to IpCidr library 2025-05-22 15:30:13 +02:00
Mikhail Epifanov 179d7fdfb8 add more realistic exclude_addresses 2025-05-22 15:18:59 +02:00
Mikhail Epifanov 2176262f22 use IpNets for filtering instead of individual IPs 2025-05-22 15:03:31 +02:00
github-actions[bot] 447283cf23 Merge pull request #786 from bee-san/dependabot/cargo/criterion-0.6.0
build(deps): bump criterion from 0.5.1 to 0.6.0
2025-05-19 09:15:54 +00:00
dependabot[bot] 8b317cec1f build(deps): bump criterion from 0.5.1 to 0.6.0
Bumps [criterion](https://github.com/bheisler/criterion.rs) from 0.5.1 to 0.6.0.
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.5.1...0.6.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-05-19 09:15:41 +00:00
github-actions[bot] 08ccf624c1 Merge pull request #785 from bee-san/dependabot/cargo/clap-4.5.38
build(deps): bump clap from 4.5.37 to 4.5.38
2025-05-12 09:41:37 +00:00
dependabot[bot] 16e45cea2b build(deps): bump clap from 4.5.37 to 4.5.38
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.37 to 4.5.38.
- [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.37...clap_complete-v4.5.38)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-05-12 09:41:26 +00:00
github-actions[bot] a5c36c3f52 Merge pull request #784 from bee-san/dependabot/cargo/toml-0.8.22
build(deps): bump toml from 0.8.20 to 0.8.22
2025-04-29 09:06:33 +00:00
dependabot[bot] 1124c46f37 build(deps): bump toml from 0.8.20 to 0.8.22
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.20 to 0.8.22.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.20...toml-v0.8.22)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-04-29 09:06:23 +00:00
github-actions[bot] 8330e4363d Merge pull request #782 from bee-san/dependabot/cargo/clap-4.5.37
build(deps): bump clap from 4.5.36 to 4.5.37
2025-04-21 11:27:15 +00:00
dependabot[bot] 08ee05ff2d build(deps): bump clap from 4.5.36 to 4.5.37
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.36 to 4.5.37.
- [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.36...clap_complete-v4.5.37)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-04-21 11:27:04 +00:00
github-actions[bot] 34c285af75 Merge pull request #781 from bee-san/dependabot/cargo/rand-0.9.1
build(deps): bump rand from 0.9.0 to 0.9.1
2025-04-18 10:00:21 +00:00
dependabot[bot] e4eeeac6ef build(deps): bump rand from 0.9.0 to 0.9.1
Bumps [rand](https://github.com/rust-random/rand) from 0.9.0 to 0.9.1.
- [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.9.0...rand_core-0.9.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-04-18 10:00:10 +00:00
github-actions[bot] a70bd8b374 Merge pull request #779 from bee-san/dependabot/cargo/clap-4.5.36
build(deps): bump clap from 4.5.35 to 4.5.36
2025-04-14 09:17:23 +00:00
dependabot[bot] 2884a448be build(deps): bump clap from 4.5.35 to 4.5.36
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.35 to 4.5.36.
- [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.35...clap_complete-v4.5.36)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-04-14 09:17:09 +00:00
github-actions[bot] 6ec01bb154 Merge pull request #776 from bee-san/dependabot/cargo/tokio-1.44.2
build(deps): bump tokio from 1.43.0 to 1.44.2
2025-04-08 02:14:53 +00:00
dependabot[bot] c09baef4f8 build(deps): bump tokio from 1.43.0 to 1.44.2
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.43.0 to 1.44.2.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.43.0...tokio-1.44.2)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.44.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-04-08 02:14:42 +00:00
github-actions[bot] cf426427ed Merge pull request #774 from bee-san/dependabot/cargo/env_logger-0.11.8
build(deps): bump env_logger from 0.11.7 to 0.11.8
2025-04-02 09:49:49 +00:00
github-actions[bot] 293056ef19 Merge pull request #773 from bee-san/dependabot/cargo/clap-4.5.35
build(deps): bump clap from 4.5.34 to 4.5.35
2025-04-02 09:49:45 +00:00
dependabot[bot] 2888336657 build(deps): bump env_logger from 0.11.7 to 0.11.8
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.7 to 0.11.8.
- [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.7...v0.11.8)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-04-02 09:49:38 +00:00
dependabot[bot] 800cb3ba39 build(deps): bump clap from 4.5.34 to 4.5.35
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.34 to 4.5.35.
- [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.34...clap_complete-v4.5.35)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-04-02 09:49:31 +00:00
github-actions[bot] 096ebbf73d Merge pull request #772 from bee-san/dependabot/cargo/once_cell-1.21.3
build(deps): bump once_cell from 1.21.2 to 1.21.3
2025-03-31 10:00:28 +00:00
dependabot[bot] 5e6cc410e7 build(deps): bump once_cell from 1.21.2 to 1.21.3
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.21.2 to 1.21.3.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.21.2...v1.21.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-03-31 10:00:16 +00:00
github-actions[bot] 5739c6175e Merge pull request #771 from bee-san/dependabot/cargo/once_cell-1.21.2
build(deps): bump once_cell from 1.21.1 to 1.21.2
2025-03-28 09:43:12 +00:00
dependabot[bot] c134747788 build(deps): bump once_cell from 1.21.1 to 1.21.2
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.21.1 to 1.21.2.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.21.1...v1.21.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-28 09:42:57 +00:00
Roger G. Coram 2c2a55723a Merge pull request #769 from druil0or/master
Update README.md
2025-03-28 08:36:08 +00:00
dunno c5fb1b006c Update README.md
Edited Wiki link in links-2
2025-03-27 13:45:46 +01:00
github-actions[bot] 083a20269f Merge pull request #768 from bee-san/dependabot/cargo/clap-4.5.34
build(deps): bump clap from 4.5.32 to 4.5.34
2025-03-27 09:40:56 +00:00
dependabot[bot] 815b4b5e05 build(deps): bump clap from 4.5.32 to 4.5.34
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.32 to 4.5.34.
- [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.32...clap_complete-v4.5.34)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-27 09:40:43 +00:00
github-actions[bot] b7734da3a7 Merge pull request #767 from bee-san/dependabot/cargo/log-0.4.27
build(deps): bump log from 0.4.26 to 0.4.27
2025-03-24 09:47:55 +00:00
dependabot[bot] 36d71894b9 build(deps): bump log from 0.4.26 to 0.4.27
Bumps [log](https://github.com/rust-lang/log) from 0.4.26 to 0.4.27.
- [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.26...0.4.27)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-24 09:47:43 +00:00
github-actions[bot] 8a6334dfe8 Merge pull request #766 from bee-san/dependabot/cargo/async-std-1.13.1
build(deps): bump async-std from 1.13.0 to 1.13.1
2025-03-17 09:39:31 +00:00
dependabot[bot] e7c4205ec5 build(deps): bump async-std from 1.13.0 to 1.13.1
Bumps [async-std](https://github.com/async-rs/async-std) from 1.13.0 to 1.13.1.
- [Release notes](https://github.com/async-rs/async-std/releases)
- [Changelog](https://github.com/async-rs/async-std/blob/main/CHANGELOG.md)
- [Commits](https://github.com/async-rs/async-std/compare/v1.13.0...v1.13.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-17 09:39:20 +00:00
Roger G. Coram 1553e58cf7 Merge pull request #764 from bee-san/dependabot/cargo/env_logger-0.11.7
build(deps): bump env_logger from 0.11.6 to 0.11.7
2025-03-16 09:30:12 +00:00
github-actions[bot] 2e5e5fd71e Merge pull request #765 from bee-san/dependabot/cargo/once_cell-1.21.1
build(deps): bump once_cell from 1.21.0 to 1.21.1
2025-03-14 09:20:53 +00:00
dependabot[bot] ea2e8f6fbe build(deps): bump once_cell from 1.21.0 to 1.21.1
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.21.0 to 1.21.1.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.21.0...v1.21.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-14 09:20:41 +00:00
github-actions[bot] d6bc7e7219 Merge pull request #763 from bee-san/dependabot/cargo/once_cell-1.21.0
build(deps): bump once_cell from 1.20.3 to 1.21.0
2025-03-11 09:33:47 +00:00
dependabot[bot] 5d66529231 build(deps): bump env_logger from 0.11.6 to 0.11.7
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.6 to 0.11.7.
- [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.6...v0.11.7)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-11 09:33:38 +00:00
github-actions[bot] 9e3c547615 Merge pull request #762 from bee-san/dependabot/cargo/clap-4.5.32
build(deps): bump clap from 4.5.31 to 4.5.32
2025-03-11 09:33:38 +00:00
dependabot[bot] a8c723fc68 build(deps): bump once_cell from 1.20.3 to 1.21.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.3 to 1.21.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.3...v1.21.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-11 09:33:31 +00:00
dependabot[bot] bbb8237d19 build(deps): bump clap from 4.5.31 to 4.5.32
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.31 to 4.5.32.
- [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/v4.5.31...clap_complete-v4.5.32)

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

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-11 09:33:24 +00:00
G. 5f8e015686 Add funny quotes (#759) 2025-03-09 10:02:24 +00:00
github-actions[bot] 9d6bca1119 Merge pull request #760 from bee-san/dependabot/cargo/ring-0.17.13
build(deps): bump ring from 0.17.11 to 0.17.13
2025-03-07 17:05:19 +00:00
dependabot[bot] 4168bf612e build(deps): bump ring from 0.17.11 to 0.17.13
Bumps [ring](https://github.com/briansmith/ring) from 0.17.11 to 0.17.13.
- [Changelog](https://github.com/briansmith/ring/blob/main/RELEASES.md)
- [Commits](https://github.com/briansmith/ring/commits)

---
updated-dependencies:
- dependency-name: ring
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-03-07 17:00:06 +00:00
Autumn (Bee)andbee 6a1b49e7a3 Add custom script directory (#758)
* Add custom script directory

* run just

---------

Co-authored-by: bee <[email protected]>
2025-03-05 09:28:57 +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
Integral b039db77f0 refactor: replace subprocess crate with std::process (#697) 2024-11-25 09:25:57 +00:00
Roger G. Coram 8350947caf build: clippy deny warnings (#698)
elevate all warnings to errors for `clippy`.
2024-11-25 07:38:51 +00:00
Autumn (Bee)andbee c7776873bb renamed testing and added CI for packaging (#688)
* renamed testing and added CI for packaging

* fix comments;

---------

Co-authored-by: bee <[email protected]>
2024-11-20 16:58:17 +00:00
Autumn (Bee) 306df13861 Update README.md 2024-11-20 06:56:51 +00:00
Roger G. Coram 88c58f3170 fix: avoid stderr in test output (#692)
as per discussions on the
[`subprocess`](https://github.com/hniksic/rust-subprocess/issues/62)
GitHub.
2024-11-19 08:57:52 +00:00
drawdropandbee 4e3c2a5649 chore: fix some comments (#644)
Signed-off-by: drawdrop <[email protected]>
Co-authored-by: bee <[email protected]>
2024-11-12 17:02:55 +00:00
Autumn (Bee)andbee d6fa72ae34 updated readme (#687)
Co-authored-by: bee <[email protected]>
2024-11-12 16:56:44 +00:00
Roger G. Coram 9ce001f60f Merge pull request #620 from israelrg/ipv6nmapfix
BugFix: Allow IPV6 addresses to be parsed into NMAP
2024-11-10 13:01:03 +00:00
Roger G. Coram f81312d63b Merge pull request #685 from RustScan/dependabot/cargo/anstream-0.6.18
build(deps): bump anstream from 0.6.17 to 0.6.18
2024-11-05 16:25:59 +00:00
Roger G. Coram 04224b6a72 Merge pull request #684 from RustScan/dependabot/github_actions/extractions/setup-just-2
build(deps): bump extractions/setup-just from 1 to 2
2024-11-05 16:25:24 +00:00
dependabot[bot] 086812f2a2 build(deps): bump anstream from 0.6.17 to 0.6.18
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.17 to 0.6.18.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.17...anstream-v0.6.18)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-05 09:50:46 +00:00
dependabot[bot] 85959b8c13 build(deps): bump extractions/setup-just from 1 to 2
Bumps [extractions/setup-just](https://github.com/extractions/setup-just) from 1 to 2.
- [Release notes](https://github.com/extractions/setup-just/releases)
- [Commits](https://github.com/extractions/setup-just/compare/v1...v2)

---
updated-dependencies:
- dependency-name: extractions/setup-just
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-05 09:30:18 +00:00
Autumn (Bee)andbee e29aa7747c Bee release justfile (#682)
* added release and fix for justfile

* Ran just fix

---------

Co-authored-by: bee <[email protected]>
2024-11-04 16:29:39 +00:00
Autumn (Bee)andbee 640003f22c Remove builds from CI and change to just (#681)
* Remove builds from CI and change to `just`

Per discussion in Discord, we are:
* Removing all official distributions to focus solely on RustScan itself
* Moving to `just` so developers can run 1 command to run all of our test suites. In some PRs it has turned into a cat and mouse game where they are not running all of our testsl

* make CI run

* update readme

* update readme

* try to fix just

* typo

* fixing just

* install nextest

---------

Co-authored-by: bee <[email protected]>
2024-11-04 14:29:33 +00:00
Roger G. Coram 66de4e7ee0 Merge pull request #669 from RustScan/dependabot/github_actions/actions/upload-artifact-4.4.3
Bump actions/upload-artifact from 4.3.4 to 4.4.3
2024-11-04 10:20:26 +00:00
dependabot[bot] 7edff6d767 Bump actions/upload-artifact from 4.3.4 to 4.4.3
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.4 to 4.4.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.3.4...v4.4.3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 10:18:25 +00:00
Roger G. Coram 5198994cbd Merge pull request #665 from RustScan/dependabot/cargo/once_cell-1.20.2
Bump once_cell from 1.19.0 to 1.20.2
2024-11-04 10:17:11 +00:00
dependabot[bot] cabeb6b1cb Bump once_cell from 1.19.0 to 1.20.2
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.19.0 to 1.20.2.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.19.0...v1.20.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 10:09:31 +00:00
Roger G. Coram 2956073d0d Merge pull request #657 from RustScan/dependabot/docker/alpine-3.20.3
Bump alpine from 3.20.2 to 3.20.3
2024-11-04 10:04:54 +00:00
dependabot[bot] 2c55f1ae58 Bump alpine from 3.20.2 to 3.20.3
Bumps alpine from 3.20.2 to 3.20.3.

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 09:59:57 +00:00
Roger G. Coram e3bd50c922 Merge pull request #656 from RustScan/dependabot/cargo/async-std-1.13.0
Bump async-std from 1.12.0 to 1.13.0
2024-11-04 09:59:13 +00:00
dependabot[bot] d5ed38ef60 Bump async-std from 1.12.0 to 1.13.0
Bumps [async-std](https://github.com/async-rs/async-std) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/async-rs/async-std/releases)
- [Changelog](https://github.com/async-rs/async-std/blob/main/CHANGELOG.md)
- [Commits](https://github.com/async-rs/async-std/commits)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 09:54:27 +00:00
Roger G. Coram e4937567b8 Merge pull request #654 from RustScan/dependabot/cargo/rlimit-0.10.2
Bump rlimit from 0.10.1 to 0.10.2
2024-11-04 09:53:38 +00:00
dependabot[bot] f90d56f02b Bump rlimit from 0.10.1 to 0.10.2
Bumps [rlimit](https://github.com/Nugine/rlimit) from 0.10.1 to 0.10.2.
- [Changelog](https://github.com/Nugine/rlimit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Nugine/rlimit/compare/v0.10.1...v0.10.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 09:38:34 +00:00
Roger G. Coram fdd74c9dab Merge pull request #640 from RustScan/dependabot/cargo/clap-4.5.13
Bump clap from 4.5.9 to 4.5.13
2024-11-04 09:37:46 +00:00
dependabot[bot] 8b253edaed Bump clap from 4.5.9 to 4.5.13
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.9 to 4.5.13.
- [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.9...v4.5.13)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 09:28:23 +00:00
Roger G. Coram 9ebc04cece Merge pull request #639 from RustScan/dependabot/cargo/toml-0.8.19
Bump toml from 0.8.14 to 0.8.19
2024-11-04 09:27:26 +00:00
dependabot[bot] 26f387adc0 Bump toml from 0.8.14 to 0.8.19
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.14 to 0.8.19.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.14...toml-v0.8.19)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 09:24:27 +00:00
Roger G. Coram a46096dab5 Merge pull request #638 from RustScan/dependabot/cargo/colorful-0.3.2
Bump colorful from 0.2.2 to 0.3.2
2024-11-04 09:23:34 +00:00
dependabot[bot] 9c0992dcd0 Bump colorful from 0.2.2 to 0.3.2
Bumps [colorful](https://github.com/mellite/colorful) from 0.2.2 to 0.3.2.
- [Commits](https://github.com/mellite/colorful/commits)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 09:20:19 +00:00
Roger G. Coram 347985ea52 Merge pull request #679 from RustScan/dependabot/cargo/anstream-0.6.17
Bump anstream from 0.6.14 to 0.6.17
2024-11-04 09:19:15 +00:00
dependabot[bot] cafcc10b2e Bump anstream from 0.6.14 to 0.6.17
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.14 to 0.6.17.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.14...anstream-v0.6.17)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-11-04 09:13:50 +00:00
mugiseyebrows 371ef81c7b fix colors in terminal on windows (#678) 2024-11-04 08:54:48 +00:00
Roger G. Coram 54bc6bf191 build: remove release on pull-request (#673)
- prevent running of the release workflow on pull-request: releases should
  only happen on explicit tagging.
- tests etc. still run as part of the build workflow.
2024-11-04 08:54:40 +00:00
Autumn (Bee)andbee 042a1c7b6c Added justfile for tests (#680)
Co-authored-by: bee <[email protected]>
2024-11-04 08:54:21 +00:00
Roger G. Coram aed0d71de8 Merge pull request #675 from mcamou/add-arm64-docker-build
Fixes #645: Add arm64 Docker image build
2024-10-26 11:52:07 +01:00
mcamou ea428a1a22 Add arm64 Docker image build 2024-10-26 12:19:04 +02:00
Roger G. Coram da41cb062d fix: avoid duplicate IP addresses (#660)
avoid scanning duplicated IPs for overlapping CIDRs.

fixes #651
2024-09-21 19:06:14 +01:00
Roger G. Coram 0a40d1539a Merge pull request #653 from socketz/patch-1
Update README.md
2024-09-19 14:13:43 +01:00
Roger G. Coram c3d24feebe Merge pull request #632 from RustScan/dependabot/cargo/env_logger-0.11.5
Bump env_logger from 0.11.3 to 0.11.5
2024-09-18 16:45:03 +01:00
BrendanGlancy 45fa80acd4 Statically generated payloads (#643)
* update the master branch to use nmap-payloads file

* code generation

* statically generate payloads, workingish

* actually puts the map in memory at compile time

* I am not sure this needs to be in the repo since its built on compilation

* actually ignores

* converting this in networking class counts as paying attention to the lecture right?

* see if if works on windows

* see

* I think i fixed CI

* fixes clippy

* ugh

* formats the gen code automagically

* UDP Service Probing

* UDP Service Probing

* Better comments

* fixes clippy

* fixes cargo fmt
2024-09-13 17:49:30 +01:00
Roger G. Coram a6f04c5c4a Merge pull request #618 from Arvmor/master
Using references in `init_scripts()` to avoid unnecessary `.clone()`s
2024-08-28 09:14:41 +01:00
socketz 0a09da40aa Update README.md
Fixed Build badge
2024-08-27 13:00:33 +02:00
dependabot[bot] 6ffd5f741e Bump env_logger from 0.11.3 to 0.11.5
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.3 to 0.11.5.
- [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.3...v0.11.5)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-08-21 18:49:48 +00:00
Roger G. Coram efc42af2aa build: allow cargo-tarpaulin config (#647) 2024-08-21 14:10:10 +01:00
Israel Rodriguez Gallego 4f964b7d47 Clean comments 2024-08-17 23:17:48 +02:00
Israel Rodriguez Gallego 3887dbb93f Remove module that is not used 2024-08-17 23:16:32 +02:00
Israel Rodriguez Gallego 9c2c03f68c Move IP version check match inside the enum declaration 2024-08-17 23:09:48 +02:00
Rui Chen d6cd9392d6 docs: fix homebrew formula link (#636) 2024-08-07 08:58:26 +01:00
dependabot[bot] 12dfefaf4d Bump alpine from 3.20.1 to 3.20.2 (#628)
Bumps alpine from 3.20.1 to 3.20.2.

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-26 11:56:04 +01:00
Paul Pfeister 663a3df7ed Match crate license to source (#616) 2024-07-26 11:55:19 +01:00
Israel Rodriguez Gallego 7d8b36aaef BugFix: Allow IPV6 addresses to be parsed into NMAP 2024-07-15 23:05:57 +02:00
Arvin Morawej 4564036218 Fix Lint errors, unused imports 2024-07-11 09:39:49 -04:00
Arvin Morawej 5b87f96451 Fix Lint errors 2024-07-11 09:36:24 -04:00
Arvin Morawej 033e2810f2 Using references in init_scripts()to avoid unnecessary .clone()s 2024-07-11 09:29:40 -04:00
Roger G. Coram 9f35cd43bc Merge pull request #617 from RustScan/dependabot/cargo/clap-4.5.9
Bump clap from 4.5.8 to 4.5.9
2024-07-09 10:46:11 +01:00
dependabot[bot] 68e26cc0fc Bump clap from 4.5.8 to 4.5.9
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.8 to 4.5.9.
- [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/v4.5.8...v4.5.9)

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

Signed-off-by: dependabot[bot] <[email protected]>
2024-07-09 09:11:22 +00:00
Autumn (Bee)andbee-san 9c63b56142 2.4.0 (#613)
Co-authored-by: bee-san <[email protected]>
2024-07-08 14:48:22 +01:00
Ærvin 8879fa7ff4 removed unnecessary .clone()s (#610) 2024-07-08 14:34:35 +01:00
dependabot[bot] 5d532356fd Bump log from 0.4.21 to 0.4.22 (#605)
Bumps [log](https://github.com/rust-lang/log) from 0.4.21 to 0.4.22.
- [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.21...0.4.22)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 14:32:39 +01:00
dependabot[bot] 9f69d36057 Bump clap from 4.5.7 to 4.5.8 (#607)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.7 to 4.5.8.
- [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.7...v4.5.8)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 14:32:28 +01:00
dependabot[bot] dc18469af6 Bump text_placeholder from 0.5.0 to 0.5.1 (#609)
Bumps [text_placeholder](https://github.com/bernardoamc/text-placeholder) from 0.5.0 to 0.5.1.
- [Commits](https://github.com/bernardoamc/text-placeholder/commits)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 14:32:20 +01:00
Neal Caffery 01fb9a14ba fix for the target x86_64-pc-windows-gnu (#611) 2024-07-08 14:32:06 +01:00
dependabot[bot] e2ddd93835 Bump actions/upload-artifact from 4.3.3 to 4.3.4 (#612)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.3 to 4.3.4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.3.3...v4.3.4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 14:31:38 +01:00
dependabot[bot] 3873117324 Bump alpine from 3.20.0 to 3.20.1 (#604)
Bumps alpine from 3.20.0 to 3.20.1.

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-26 07:57:48 +01:00
dependabot[bot] dcb91d95e4 Bump docker/build-push-action from 5 to 6 (#603)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-26 07:57:23 +01:00
dependabot[bot] 4947158ff8 Bump clap from 4.5.6 to 4.5.7 (#601)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.6 to 4.5.7.
- [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/v4.5.6...v4.5.7)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-26 07:57:09 +01:00
BrendanGlancy f544f2240b Udp Scanning (#596)
Added UDP scanning support to RustScan
2024-06-25 15:16:06 +01:00
dependabot[bot] f33476468f Bump clap from 4.5.4 to 4.5.6 (#600)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.4 to 4.5.6.
- [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.4...v4.5.6)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 12:00:05 +01:00
dependabot[bot] 534cf8adf8 Bump alpine from 3.19.1 to 3.20.0 (#597)
Bumps alpine from 3.19.1 to 3.20.0.

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-07 09:23:58 +01:00
dependabot[bot] 8290cad8d6 Bump toml from 0.8.13 to 0.8.14 (#599)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.13 to 0.8.14.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.13...toml-v0.8.14)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-07 09:23:50 +01:00
LeoFVO ac3ad3a6f7 ci: refactor ci/cd and extends matrix build (#598)
* ci(release): refactor ci/cd and extends matrix build

- adding ARM release
- fix build/test issues during release
- adding binary (linux + exe) in the release assets
- uploading all cross-build assets via archive (zip + tar.xz)

Signed-off-by: LeoFVO <[email protected]>

* fix(ci): upload assets to release only in case of release

Signed-off-by: LeoFVO <[email protected]>

---------

Signed-off-by: LeoFVO <[email protected]>
2024-06-06 21:31:10 +01:00
Autumn (Bee) 3e74f5cb69 Update dockere in README (#595) 2024-05-20 13:20:34 +01:00
dependabot[bot] bfb827c0b8 Bump toml from 0.8.12 to 0.8.13 (#592)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.12 to 0.8.13.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.12...toml-v0.8.13)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-18 15:18:47 +01:00
dependabot[bot] 16250456e6 Bump itertools from 0.12.1 to 0.13.0 (#593)
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.1 to 0.13.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.1...v0.13.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-18 15:18:37 +01:00
Autumn (Bee) 0779071f7f 2.2.4-rc1 2024-05-18 14:49:03 +01:00
Roger G. Coram 8bfb643796 refactor: remove structopt in favour of clap (#591)
* refactor: remove structopt in favour of clap

as per
[`structopt` README](https://github.com/TeXitoi/structopt#maintenance),
it is now in maintenance mode.

migrating to `clap` v3 as per their
[migration docs.](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md#300---2021-12-31).

* refactor: upgrade clap to v4

implement `clap` breaking changes as per docs.

* test: add parameterized tests for nmap-command parsing
2024-05-18 14:47:10 +01:00
Roger G. Coram 457c1643ff chore: upgrade dependencies (#590)
- move to the latest versions of direct dependencies.
  - update `rand` usage to use `Range` types.
  - update `cidr_utils` to reflect underlying `cidr` crate.
2024-05-15 07:16:22 +01:00
Roger G. CoramandKhanh Nguyen 4ae4853e1d feat(cli): add custom dns resolver option (#587)
* feat(cli): add custom dns resolver option

* fix: correct clippy lints

* refactor: update resolvers to be Option

use an `Option<String>` rather than a `String`, defaulting to `""`.

* feat(cli): include resolver in config.

* refactor: change resolver chain

- remove extraneous `std::net::` prefixes for already-imported `IpAddr`.
- move derivation to a function:
  - parse input/file as normal;
  - attempt to use the system DNS resolution;
  - fall back to previous CloudFlare behaviour.

---------

Co-authored-by: Khanh Nguyen <[email protected]>
2024-05-10 16:14:16 +01:00
Roger G. Coram d471065dd2 refactor: move address-related functions (#585)
* refactor: move address-related functions

move functions for address-parsing/-resolution to a submodule.

fix #580

* test: add documentation tests
2024-05-09 08:14:42 +01:00
dependabot[bot] 99b00c091c Bump env_logger from 0.8.4 to 0.11.3 (#583)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.8.4 to 0.11.3.
- [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.8.4...v0.11.3)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-05 13:50:57 +01:00
dependabot[bot] af34aa4d07 Bump toml from 0.5.11 to 0.8.12 (#582)
Bumps [toml](https://github.com/toml-rs/toml) from 0.5.11 to 0.8.12.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.5.11...toml-v0.8.12)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-05 13:50:42 +01:00
Autumn (Bee) 5328bfc8b0 2.2.3 (#581) 2024-05-01 15:49:36 +01:00
dependabot[bot] 2340f94a81 Bump actions/upload-artifact from 4.3.2 to 4.3.3 (#578)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.3.2...v4.3.3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-27 10:21:23 +01:00
dependabot[bot] dcc558cb43 Bump rustls from 0.21.10 to 0.21.11 (#574)
Bumps [rustls](https://github.com/rustls/rustls) from 0.21.10 to 0.21.11.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.21.10...v/0.21.11)

---
updated-dependencies:
- dependency-name: rustls
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-21 08:28:38 +01:00
Autumn (Bee) bb2973049c Remove docker build from PRs (#573) 2024-04-21 08:28:05 +01:00
dependabot[bot] a26ea80036 Bump actions/upload-artifact from 4.3.1 to 4.3.2 (#571)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.1 to 4.3.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.3.1...v4.3.2)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 16:36:46 +01:00
toofooboo 2643a1d5e1 chore: remove repetitive word (#572)
Signed-off-by: toofooboo <[email protected]>
2024-04-19 16:36:34 +01:00
Roger G. Coram b9337d8267 refactor: make RustScan additionally a library (#562)
* refactor: make RustScan additionally a library

- add `lib.rs` (permitted as per
  [_Packages and Crates_](https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html)).
- refactor `main.rs` to reference the library.
- update module references as required.

* doc: correct documentation issues

- fix some failing doc-tests.
- amend formatting causing misinterpretation as code-samples.
- mark some examples as
  [`compile_fail`](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html#attributes)
  as there's no immediate way to instantiate the required `struct`.

* docs: add basic module documentation

- add lib-level docs. including a minimal example.
- add minimal, headline information for each module.
2024-04-14 10:09:21 +01:00
dependabot[bot] e4a097c7dc Bump Swatinem/rust-cache from 1 to 2 (#563)
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 1 to 2.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v1...v2)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 17:58:18 +01:00
dependabot[bot] 15290b2275 Bump actions/download-artifact from 2 to 4 (#564)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 17:58:06 +01:00
dependabot[bot] 6993fb2037 Bump mislav/bump-homebrew-formula-action from 1 to 3 (#565)
Bumps [mislav/bump-homebrew-formula-action](https://github.com/mislav/bump-homebrew-formula-action) from 1 to 3.
- [Release notes](https://github.com/mislav/bump-homebrew-formula-action/releases)
- [Commits](https://github.com/mislav/bump-homebrew-formula-action/compare/v1...v3)

---
updated-dependencies:
- dependency-name: mislav/bump-homebrew-formula-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 17:57:55 +01:00
dependabot[bot] a311726486 Bump actions/setup-python from 4 to 5 (#566)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 17:57:42 +01:00
dependabot[bot] 030e9708f9 Bump actions/upload-artifact from 2.2.4 to 4.3.1 (#567)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2.2.4 to 4.3.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.2.4...v4.3.1)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 17:57:31 +01:00
Juan Calderon-Perez 7f80dd1678 Upgrade Alpine to 3.19.x (#373)
* Update Dockerfile

Upgrade alpine to 3.13, add ca-certificates and bind-tools.

* Add github/docker to dependabot. Update some of the actions

* Skip Registry login for Pull Requests
2024-04-11 17:19:58 +01:00
Ben (CMNatic) f5a587accf Merge pull request #561 from PsypherPunk/update-docker-workflow
build: update Docker workflow trigger
2024-04-10 19:49:26 +01:00
PsypherPunk 1da08dcb9a build: update Docker workflow trigger
remove the `v` prefix from the triggering tag.
2024-04-10 13:24:55 +01:00
Ben (CMNatic) 6ca22d697c Update Docker Runner (#560) 2024-04-10 10:29:19 +01:00
nullandAutumn ac38ca591d Fixed #527 (#537)
* Fixed #527

* Update mod.rs

---------

Co-authored-by: Autumn (Bee) <[email protected]>
2024-04-10 09:31:48 +01:00
Roger G. Coram f2d9a2e1fa Update release workflow (#559)
* build: remove explicit strip steps

- update `Cargo.toml` to
  [`strip`](https://doc.rust-lang.org/cargo/reference/profiles.html#strip)
  as part of the release profile.
- remove explicit `strip` steps from the release workflow.
- update step name to better reflect `git config` changes.

* build: remove prefix from expected tags

- remove the `v` prefix from the triggering tag (all previous tags lack this).
- update tag-deriving steps to better reflect the expected tag pattern.
- update tag-deriving steps to be consistent in how `GITHUB_REF` is parsed.

* build: make build/OS checks generic

- update GitHub Actions conditions to use
  [`contains`](https://docs.github.com/en/actions/learn-github-actions/expressions#contains)
  to match _all_ related builds.
- update shell conditions to accommodate the same.
2024-04-10 09:13:06 +01:00
Lokendra Singh Rawat e3c05afabb minor change in help section (#318)
Changed this : 
For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"")
to:
For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"
2024-04-10 07:23:31 +01:00
c466e0641e fix help message (#319) (#330)
Co-authored-by: Bee <[email protected]>
Co-authored-by: Autumn (Bee) <[email protected]>
2024-04-10 07:22:38 +01:00
dependabot[bot] 331efa5011 Bump text_placeholder from 0.4.1 to 0.5.0 (#558)
Bumps [text_placeholder](https://github.com/bernardoamc/text-placeholder) from 0.4.1 to 0.5.0.
- [Commits](https://github.com/bernardoamc/text-placeholder/commits)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-10 07:16:56 +01:00
dependabot[bot] fa1ddc9bba Bump colored from 2.0.0 to 2.1.0 (#557)
Bumps [colored](https://github.com/mackwic/colored) from 2.0.0 to 2.1.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.0.0...v2.1.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-10 07:16:23 +01:00
Wuelfhis AsuajeandWuelfhis Asuaje 170f57c0a3 First attempt enabling exclude port list capability Issue #530 (#534)
* First attempt enabling exclude port list capability

* disabling a not desired println! used for debugging purpose

* fixing small typo:  dbg! by debug!

* adding emtpy commit to make CI/CD pass

* running cargo fmt and removing a TODO comment

* Actually running cargo fmt without check LoL

* fixed some clippy error

* fixing one more cargo clippy complain

---------

Co-authored-by: Wuelfhis Asuaje <[email protected]>
2024-04-10 07:15:43 +01:00
dependabot[bot] 6b6b2f267c Bump text_placeholder from 0.4.0 to 0.4.1 (#552)
Bumps [text_placeholder](https://github.com/bernardoamc/text-placeholder) from 0.4.0 to 0.4.1.
- [Commits](https://github.com/bernardoamc/text-placeholder/commits)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 08:20:02 +01:00
dependabot[bot] 2287965c5c Bump trust-dns-resolver from 0.21.2 to 0.23.2 (#554)
Bumps [trust-dns-resolver](https://github.com/bluejekyll/trust-dns) from 0.21.2 to 0.23.2.
- [Release notes](https://github.com/bluejekyll/trust-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.23.2/CHANGELOG.md)
- [Commits](https://github.com/bluejekyll/trust-dns/compare/v0.21.2...v0.23.2)

---
updated-dependencies:
- dependency-name: trust-dns-resolver
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 08:19:38 +01:00
Roger G. Coram 1ec8ab412e feat(build): add GitHub Action to make release (#427)
* build: move cargo-deb to main release workflow

* build: use absolute paths for deb package

* build: remove Docker builder for deb files

as [requested](https://github.com/RustScan/RustScan/pull/427#issuecomment-2041374743).
2024-04-09 08:19:01 +01:00
Maximiliano Molina 598b5dfd10 fix port range to include end (#518)
* fix port range to include end

* empty

Signed-off-by: Maximiliano Molina <[email protected]>

---------

Signed-off-by: Maximiliano Molina <[email protected]>
2024-04-07 08:10:39 +01:00
fabianbees 1a5ff5bb4b initialize errors HashSet without high initial capacity (#532)
fixes:
- #447
- #523
2024-04-07 08:10:26 +01:00
Jeremiah Gleason 10b3f82bb7 Added more funny quotes. (#526) 2024-04-06 21:58:59 +01:00
Ben (CMNatic) 0e582fdd16 Fix markdown-link-checker workflow (#542)
* Fix markdown-link-checker workflow

Added a configuration file that tells MLC to ignore the URLs in .github\workflows\mlc_config.json as MLC is falsely reporting these as dead

* Update markdown.yml
2024-04-06 21:28:53 +01:00
Ben (CMNatic) 4c933b39a1 Merge pull request #538 from bergabman/master
Fix clippy lint issues for CI/CD pipeline
2024-03-16 01:56:02 +00:00
Ben (CMNatic) a3495b305b Merge branch 'master' into master 2024-03-16 00:31:38 +00:00
Ben (CMNatic) 4bec0a8ea8 Merge pull request #525 from R-Rothrock/master
Fixed #524 - banner formatting
2024-03-16 00:11:55 +00:00
bergabman e86b86d0e0 clippy lint issues 2024-02-13 17:35:35 +01:00
Roan Rothrock 43d3a1ed5a Fixed #524 2023-08-25 16:18:51 -05:00
Thaddeus Thomas 0f65753dcb Update README.md (#520)
* Update README.md

Fixed the issue with the links
closes #516

* Update README.md

1. Fixed the arch Linux link. Closes #519
2. Fixed Arch badge so it's linked to the correct version
3. Cleaned up missing links throughout the document
4. Moved all links other than the contributor's section to the end of the document for easier maintenance
5. Fixed language so it was grammatically correct
2023-08-12 08:30:30 +01:00
LePirlouit 2ab7191a65 🚨 Fix linter warning (#521) 2023-07-10 20:02:23 +01:00
♠️ Thomas Gotwig ♣️andThomas Gotwig d7c07c4c84 📝 Change broken link to tgotwig.dev (#398)
Co-authored-by: Thomas Gotwig <[email protected]>
2023-06-18 19:08:16 +01:00
LePirlouitandAutumn 478851ac4c 🚨 fix linter warnings (#511)
Co-authored-by: Autumn <[email protected]>
2023-06-18 19:07:35 +01:00
Thaddeus Thomas 1c7b080d6f Update README.md (#517)
Fixed the issue with the links
closes #516
2023-06-18 19:06:21 +01:00
Ben (CMNatic) 7dd9352bae Update recommended Docker image version
Version 2.1.1 of RustScan has been uploaded to the DockerHub repo: https://hub.docker.com/layers/rustscan/rustscan/2.1.1/images/sha256-1ef645b90001c560923c2150d20e5509ced243122225dbb9daf7c1c0ac3d21b2?context=repo
2023-04-06 13:27:06 +01:00
Kian-Meng Ang d3fdc0d796 Fix typos (#505)
Found via `typos --format brief`
2023-03-04 18:19:36 +00:00
Laurent Erignoux 77853ad122 Minor documentation table fix for github.io (#491)
* Adding missing paragraph closing in Readme

* Using markdown :-: to center Readme packages table content
2023-02-02 07:40:33 +00:00
Peter Dave Hello 71f21aa259 Update Docker base image from alpine 3.12 to 3.17 (#484)
Alpine Linux v3.12 is end of life about months, the builder base image
is alreayd Alpine v3.17, and Alpine v3.17 is the latest stable release
with about 2 more years support, should be good to ugprade to it.

Signed-off-by: Peter Dave Hello <[email protected]>

Signed-off-by: Peter Dave Hello <[email protected]>
2022-12-30 09:18:49 +00:00
dependabot-preview[bot]andBee bcdb0df1d9 Upgrade to GitHub-native Dependabot (#382)
Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Bee <[email protected]>
2022-12-23 11:42:04 +00:00
LeoFVO 5b00fa1727 build and publish docker image in ci (#472)
* feat(docker): build and publish docker image in ci

Signed-off-by: leofvo <[email protected]>

* refactor(docker): updated dockerfile to build release version of rustscan

Signed-off-by: leofvo <[email protected]>
2022-12-20 20:17:08 +00:00
LeoFVO dde7d96256 fix(doc): minor lint fixes (#470)
Signed-off-by: leofvo <[email protected]>

Signed-off-by: leofvo <[email protected]>
2022-12-08 08:01:58 +00:00
LeoFVO 405f05d42c Build CI to auto-distribute releases (#468)
* refactor(ci): update build ci and add cross-compilation

* cron check
* remove fmt
* cross-compile for tests
* lint code
* remove duplicate

Signed-off-by: leofvo <[email protected]>

* fix(code): code lint + fix bad practices

Signed-off-by: leofvo <[email protected]>

* test(windows): remove windows incompatible function for test when running on windows

Signed-off-by: leofvo <[email protected]>

* build(release): add release ci/cd

* github workflow
* cargo.toml improvment

Signed-off-by: leofvo <[email protected]>

Signed-off-by: leofvo <[email protected]>
2022-12-06 08:45:57 +00:00
Bee 9e1cb60a05 Update for ARM; (#464) 2022-11-13 18:54:56 +00:00
autumn skerritt 8045af8cd2 Add release automation file 2022-11-12 16:41:14 +00:00
autumn skerritt 221cf8694f Updating lock file 2022-11-12 16:36:44 +00:00
Bee bd32f4ead4 2.1.1 2022-11-07 10:21:47 +00:00
Artem IvanovandBee 8625f939bf Minor cosmetic changes (#453)
* Minor cosmetic changes

* Minor cosmetic changes

* Update discord link

Co-authored-by: Bee <[email protected]>
2022-11-07 10:16:14 +00:00
Thijs0x57andBee 8f608eaf99 fix links to Docker & Kali wiki (#433)
In the readme there are 2 links to docker & kali, 'Usage' and 'Read the install guide'. These links weren't working.

Co-authored-by: Bee <[email protected]>
2022-11-03 10:08:03 +00:00
NugineandBee f9f2bf6cf8 Upgrade rlimit (#449)
Co-authored-by: Bee <[email protected]>
2022-11-03 09:56:54 +00:00
Callum Haughtey-WilkinsonandBee 6fb1196244 Improve doc for addresses (#457)
Improve documentation for addresses CLI arg to make it
clear that file parsing of addresses is already supported

(#452)

Co-authored-by: Bee <[email protected]>
2022-11-02 15:23:02 +00:00
Callum Haughtey-Wilkinson 61c43dd5d0 Allow CLI customisation of config file path (#456)
Allow CLI customisation of config file path with new
optional argument.

(#437)
2022-11-02 15:17:07 +00:00
46 changed files with 5775 additions and 2041 deletions

No files matched your search

+4
View File
@@ -7,6 +7,10 @@ assignees: ''
---
IMPORTANT: If you do not fill this out, your issue will be automatically closed.
As of November 2024, RustScan no longer offiically supports any packages other than `cargo`. If you have an issue with Docker or a `.deb`, please bring it up with whoever created it :)
**Describe the bug**
A clear and concise description of what the bug is.
+29
View File
@@ -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
-10
View File
@@ -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
-48
View File
@@ -1,48 +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
+201
View File
@@ -0,0 +1,201 @@
name: CD Pipeline
on:
push:
branches:
- "master"
tags:
- "*"
# Build jobs only need to read the repository. The publish job below is the
# only job that gets write access, and it only runs for tag pushes.
permissions:
contents: read
jobs:
build-nix:
env:
IN_PIPELINE: true
runs-on: ${{ matrix.os }}
strategy:
matrix:
type: [ubuntu-x64, ubuntu-x86, armv7, aarch64]
include:
- type: ubuntu-x64
os: ubuntu-latest
target: x86_64-unknown-linux-musl
name: x86_64-linux-rustscan
path: target/x86_64-unknown-linux-musl/release/rustscan
pkg_config_path: /usr/lib/x86_64-linux-gnu/pkgconfig
- type: ubuntu-x86
os: ubuntu-latest
target: i686-unknown-linux-musl
name: x86-linux-rustscan
path: target/i686-unknown-linux-musl/release/rustscan
pkg_config_path: /usr/lib/i686-linux-gnu/pkgconfig
- type: armv7
os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
name: armv7-linux-rustscan
path: target/armv7-unknown-linux-gnueabihf/release/rustscan
pkg_config_path: /usr/lib/x86_64-linux-gnu/pkgconfig
- type: aarch64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
name: aarch64-linux-rustscan
path: target/aarch64-unknown-linux-gnu/release/rustscan
pkg_config_path: /usr/lib/x86_64-linux-gnu/pkgconfig
steps:
- uses: actions/checkout@v7
- name: Cache cargo & target directories
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Build binary
uses: houseabsolute/actions-rust-cross@21b0f18dc621b25bfae556ff2791fca4173121e8 # v1.0.8
with:
command: build
target: ${{ matrix.target }}
args: "--locked --release"
strip: true
toolchain: stable
- name: Package and upload artifacts
uses: houseabsolute/actions-rust-release@9e126fd5fc18f4cecf358b3909f3991ad59663dc # v1.0.1
with:
executable-name: rustscan
target: ${{ matrix.target }}
archive-name: ${{ matrix.name }}
changes-file: ""
build-deb:
needs: [build-nix]
runs-on: ubuntu-latest
env:
IN_PIPELINE: true
steps:
- uses: actions/checkout@v7
- name: Install cargo-deb
run: cargo install -f cargo-deb
- uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: musl-tools # provides musl-gcc
version: 1.0
- name: Install musl toolchain
run: rustup target add x86_64-unknown-linux-musl
- name: Deb Build
run: cargo deb --target=x86_64-unknown-linux-musl
- name: Upload Deb Artifact
uses: actions/upload-artifact@v7
with:
name: rustscan.deb
path: ./target/x86_64-unknown-linux-musl/debian/*
build-macos:
env:
IN_PIPELINE: true
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- name: Cache cargo & target directories
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Build binary
uses: houseabsolute/actions-rust-cross@21b0f18dc621b25bfae556ff2791fca4173121e8 # v1.0.8
with:
command: build
target: x86_64-apple-darwin
args: "--locked --release"
strip: true
toolchain: stable
- name: Package and upload artifacts
uses: houseabsolute/actions-rust-release@9e126fd5fc18f4cecf358b3909f3991ad59663dc # v1.0.1
with:
executable-name: rustscan
target: x86_64-apple-darwin
archive-name: x86_64-macos-rustscan
changes-file: ""
build-macos-aarch64:
env:
IN_PIPELINE: true
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- name: Cache cargo & target directories
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Build binary
uses: houseabsolute/actions-rust-cross@21b0f18dc621b25bfae556ff2791fca4173121e8 # v1.0.8
with:
command: build
target: aarch64-apple-darwin
args: "--locked --release"
strip: true
toolchain: stable
- name: Package and upload artifacts
uses: houseabsolute/actions-rust-release@9e126fd5fc18f4cecf358b3909f3991ad59663dc # v1.0.1
with:
executable-name: rustscan
target: aarch64-apple-darwin
archive-name: aarch64-macos-rustscan
changes-file: ""
build-windows:
env:
IN_PIPELINE: true
runs-on: ${{ matrix.os }}
strategy:
matrix:
type: [windows-x64, windows-x86]
include:
- type: windows-x64
os: windows-latest
target: x86_64-pc-windows-msvc
name: x86_64-windows-rustscan
- type: windows-x86
os: windows-latest
target: i686-pc-windows-msvc
name: x86-windows-rustscan
steps:
- uses: actions/checkout@v7
- name: Cache cargo & target directories
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Build binary
uses: houseabsolute/actions-rust-cross@21b0f18dc621b25bfae556ff2791fca4173121e8 # v1.0.8
with:
command: build
target: ${{ matrix.target }}
args: "--locked --release"
strip: true
toolchain: stable
- name: Package and upload artifacts
uses: houseabsolute/actions-rust-release@9e126fd5fc18f4cecf358b3909f3991ad59663dc # v1.0.1
with:
executable-name: rustscan
target: ${{ matrix.target }}
archive-name: ${{ matrix.name }}
changes-file: ""
# Runs only for tag pushes (tags can only be created by maintainers), and
# only after every build job succeeded, so a release never ships partially.
publish:
if: github.ref_type == 'tag'
needs:
- build-nix
- build-deb
- build-macos
- build-macos-aarch64
- build-windows
runs-on: ubuntu-latest
permissions:
contents: write # create the release / upload release assets
steps:
- name: Download build artifacts
uses: actions/download-artifact@v8
with:
merge-multiple: true
- name: Publish artifacts to GitHub release
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
fail_on_unmatched_files: true
files: |
*.deb
*.sha256
*.tar.gz
*.zip
-18
View File
@@ -1,18 +0,0 @@
name: Check Markdown links
on:
push:
branches:
- master
schedule:
# Run everyday at 9:00 AM (See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07)
- cron: "0 9 * * *"
jobs:
markdown-link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
use-quiet-mode: 'yes'
+14
View File
@@ -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"
}
]
}
+45
View File
@@ -0,0 +1,45 @@
name: Test
on:
pull_request:
branches: [master]
push:
branches:
- master
permissions:
contents: read
jobs:
test:
name: Test Suite
strategy:
matrix:
# ubuntu-24.04-arm is the GitHub-hosted arm64 Linux runner;
# macos-latest already runs on Apple Silicon (arm64).
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
rust: [stable]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v7
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0
with:
toolchain: ${{ matrix.rust }}
components: clippy, rustfmt
# Caching is handled by Swatinem/rust-cache below.
cache: false
# Keep RUSTFLAGS unset; `just test` already denies Clippy warnings.
rustflags: ""
- uses: taiki-e/install-action@nextest
- uses: Swatinem/rust-cache@v2
- name: Install Just
uses: extractions/setup-just@v4
- name: Run tests
run: just test
+4
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
@@ -120,3 +122,5 @@ fabric.properties
.idea/*
.idea
.idea*
settings.json
-5
View File
@@ -17,12 +17,7 @@ jobs:
script:
- cargo build
- cargo test
- "ulimit -n 5000; cargo test timelimits:: -- --ignored --test-threads=1 --show-output"
- cargo fmt -- --check
after_failure:
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
- chmod +x send.sh
- "./send.sh failure $WEBHOOK_URL"
after_success: |
if [ -n "${GITHUB_TOKEN}" ]; then
[ "${TRAVIS_BRANCH} " = master ] &&
Generated
+1881 -755
View File
File diff suppressed because it is too large. Load diff
+35 -21
View File
@@ -1,15 +1,15 @@
[package]
name = "rustscan"
version = "2.1.0"
authors = ["Bee <brando[email protected]>"]
version = "2.4.1"
authors = ["Autumn <autum[email protected]>"]
edition = "2018"
description = "Faster Nmap Scanning with Rust"
homepage = "https://github.com/rustscan/rustscan"
repository = "https://github.com/rustscan/rustscan"
license = "MIT"
license = "GPL-3.0-only"
keywords = ["port", "scanning", "nmap"]
categories = ["command-line-utilities"]
readme="README.md"
readme = "README.md"
exclude = [
".github/*",
"pictures/*",
@@ -17,31 +17,32 @@ exclude = [
]
[dependencies]
colored = "2.0.0"
structopt = "0.3.20"
async-std = "1.7.0"
clap = { version = "4.6.1", features = ["derive", "wrap_help"] }
colored = "3.1.1"
async-std = "1.13.2"
futures = "0.3"
rlimit = "0.5.4"
shell-words = "1.0.0"
log = "0.4.0"
env_logger = "0.8.2"
dirs = "3.0.1"
rlimit = "0.11.0"
log = "0.4.32"
env_logger = "0.11.10"
anstream = "=1.0.0"
dirs = "7.0.0"
gcd = "2.0.1"
rand = "0.7.3"
colorful = "0.2.1"
rand = "0.10.1"
colorful = "0.3.2"
ansi_term = "0.12.1"
toml = "0.5.7"
toml = "1.1.2"
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"] }
cidr-utils = "0.7.1"
itertools = "0.15.0"
hickory-resolver = { version = "0.24.3", 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"] }
once_cell = "1.21.4"
[dev-dependencies]
wait-timeout = "0.2"
parameterized = "2.0.0"
criterion = { version = "0.8", features = ["html_reports"] }
[package.metadata.deb]
depends = "$auto, nmap"
@@ -50,3 +51,16 @@ 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"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(tarpaulin_include)"] }
[[bench]]
name = "benchmark_helpers"
harness = false
-19
View File
@@ -1,19 +0,0 @@
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
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
RUN cargo install --path .
FROM alpine:3.12
LABEL author="Hydragyrum <https://github.com/Hydragyrum>"
RUN addgroup -S rustscan && \
adduser -S -G rustscan rustscan && \
ulimit -n 100000 && \
apk add --no-cache nmap nmap-scripts wget
COPY --from=builder /usr/local/cargo/bin/rustscan /usr/local/bin/rustscan
USER rustscan
ENTRYPOINT [ "/usr/local/bin/rustscan" ]
+118 -72
View File
@@ -1,38 +1,48 @@
<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]
</div>
# 🤔 What is this?
![fast](pictures/fast.gif)
![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).
# 🛠️ 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)
RustScan only officially supports Cargo installations, if you want to use that please install Rust and then `cargo install rustscan`
Example installations include:
MacOS:
```
brew install rustscan
```
Arch:
```
pacman -S rustscan
```
# ✨ Features
- Scans all 65k ports in **3 seconds**.
@@ -43,17 +53,18 @@ 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][toc-install] | :books: [Documentation][links-table-2] | :parrot: [Discord][discord] |
| | :penguin: [ARM Support](docs/arm-support.md) | |
## 🙋 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]
- 🐧 [ARM Support](docs/arm-support.md)
- 🦜 [Discord][discord]
- 🤸 [Usage][usage-1]
# 🔭 Why RustScan?
@@ -63,83 +74,70 @@ Not to mention RustScan uses Adaptive Learning to improve itself over time, maki
## 🧋 Speed
![fast](pictures/fast.gif)
![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](pictures/scripts.gif)
![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](pictures/adaptive.gif)
![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](pictures/accessible.gif)
![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.
# 📖 Full Installation Guide
You can find our guide [here](https://github.com/RustScan/RustScan/wiki/Installation-Guide).
## 🦊 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.
- [OpenSuse](https://software.opensuse.org/package/rustscan?search_term=rustscan)
- [Fedora/CentOS](https://copr.fedorainfracloud.org/coprs/atim/rustscan/)
[![Packaging status](https://repology.org/badge/vertical-allrepos/rustscan.svg)](https://repology.org/project/rustscan/versions)
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.
# 🤸 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 -->
[![All Contributors](https://img.shields.io/badge/all_contributors-26-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -149,7 +147,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://skerritt.blog"><img src="https://avatars3.githubusercontent.com/u/10378052?v=4" width="100px;" alt=""/><br /><sub><b>Brandon</b></sub></a><br /><a href="#infra-brandonskerritt" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/RustScan/RustScan/commits?author=brandonskerritt" title="Tests">⚠️</a> <a href="https://github.com/RustScan/RustScan/commits?author=brandonskerritt" title="Code">💻</a> <a href="#design-brandonskerritt" title="Design">🎨</a></td>
<td align="center"><a href="https://skerritt.blog"><img src="https://avatars3.githubusercontent.com/u/10378052?v=4" width="100px;" alt=""/><br /><sub><b>Bee</b></sub></a><br /><a href="#infra-beeskerritt" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/RustScan/RustScan/commits?author=beeskerritt" title="Tests">⚠️</a> <a href="https://github.com/RustScan/RustScan/commits?author=beesan" title="Code">💻</a> <a href="#design-beeskerritt" title="Design">🎨</a></td>
<td align="center"><a href="https://sakiir.ovh"><img src="https://avatars1.githubusercontent.com/u/9950578?v=4" width="100px;" alt=""/><br /><sub><b>SakiiR</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=SakiiR" title="Code">💻</a> <a href="https://github.com/RustScan/RustScan/issues?q=author%3ASakiiR" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/smackhack"><img src="https://avatars2.githubusercontent.com/u/48143394?v=4" width="100px;" alt=""/><br /><sub><b>smackhack</b></sub></a><br /><a href="#ideas-smackhack" title="Ideas, Planning, & Feedback">🤔</a> <a href="#example-smackhack" title="Examples">💡</a></td>
<td align="center"><a href="http://bernardoamc.github.io/"><img src="https://avatars0.githubusercontent.com/u/428984?v=4" width="100px;" alt=""/><br /><sub><b>Bernardo Araujo</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=bernardoamc" title="Code">💻</a> <a href="https://github.com/RustScan/RustScan/issues?q=author%3Abernardoamc" title="Bug reports">🐛</a> <a href="#design-bernardoamc" title="Design">🎨</a></td>
@@ -159,7 +157,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 +184,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://formulae.brew.sh/formula/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://github.com/bee-san/RustScan/wiki "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/actions/workflows/build.yml/badge.svg?branch=master
+89
View File
@@ -0,0 +1,89 @@
use criterion::{criterion_group, criterion_main, Criterion};
use rustscan::generated::get_parsed_data;
use rustscan::input::{Opts, PortRanges, ScanOrder};
use rustscan::port_strategy::PortStrategy;
use rustscan::scanner::build_udp_payload_lookup;
use std::collections::BTreeMap;
use std::hint::black_box;
use std::net::IpAddr;
use std::time::Duration;
fn bench_address() {
let _addrs = ["127.0.0.1".parse::<IpAddr>().unwrap()];
}
fn bench_port_strategy() {
let range = PortRanges(vec![(1, 1_000)]);
let _strategy = PortStrategy::pick(&Some(range.clone()), None, ScanOrder::Serial);
}
fn bench_address_parsing() {
let opts = Opts {
addresses: vec![
"127.0.0.1".to_owned(),
"10.2.0.1".to_owned(),
"192.168.0.0/24".to_owned(),
],
exclude_addresses: Some(vec![
"10.0.0.0/8".to_owned(),
"172.16.0.0/12".to_owned(),
"192.168.0.0/16".to_owned(),
"172.16.0.1".to_owned(),
]),
..Default::default()
};
let _ips = rustscan::address::parse_addresses(&opts);
}
// Replicates the old UDP payload selection behavior:
// scan the whole UDP payload map and find the last payload whose port list contains `port`.
fn old_payload_for_port(udp_map: &'static BTreeMap<Vec<u16>, Vec<u8>>, port: u16) -> &'static [u8] {
let mut payload: &'static [u8] = b"";
for (ports, value) in udp_map.iter() {
if ports.contains(&port) {
payload = value.as_slice();
}
}
payload
}
fn criterion_benchmark(c: &mut Criterion) {
// Benching helper functions
c.bench_function("parse address", |b| b.iter(bench_address));
c.bench_function("port strategy", |b| b.iter(bench_port_strategy));
let mut address_group = c.benchmark_group("address parsing");
address_group.measurement_time(Duration::from_secs(10));
address_group.bench_function("parse addresses with exclusions", |b| {
b.iter(bench_address_parsing)
});
address_group.finish();
// UDP payload lookup micro-benchmark: compares the old linear scan of the
// payload map with the precomputed port -> payload lookup. No sockets.
let udp_map = get_parsed_data();
let lookup = build_udp_payload_lookup(udp_map);
let ports: Vec<u16> = (1..=4096).collect();
c.bench_function("udp payload lookup/old scan map 1..4096", |b| {
b.iter(|| {
for &p in ports.iter() {
let payload = old_payload_for_port(black_box(udp_map), black_box(p));
black_box(payload);
}
})
});
c.bench_function("udp payload lookup/new hashmap 1..4096", |b| {
b.iter(|| {
for &p in ports.iter() {
let payload = lookup.get(&p).copied().unwrap_or(b"");
black_box(payload);
}
})
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
+306
View File
@@ -0,0 +1,306 @@
use std::collections::BTreeMap;
use std::fs::{self, File};
use std::env;
use std::io::{BufReader, Read};
use std::path::PathBuf;
use std::process::Command;
// Reads in a file with payloads based on port
pub fn main() {
let mut file_path = env::current_dir().expect("cant find curr dir");
file_path.push("./nmap-payloads");
let mut data = String::new();
let file = File::open(&file_path).expect("File not found.");
let mut file_buf = BufReader::new(file);
file_buf
.read_to_string(&mut data)
.expect("unable to read file");
let mut fp_map: BTreeMap<i32, String> = BTreeMap::new();
let mut count = 0;
let mut capturing = false;
let mut curr = String::new();
for line in data.trim().split('\n') {
// Strip a trailing carriage return so CRLF checkouts parse
// identically to LF ones (otherwise `\r` pollutes port tokens and
// blank lines stop looking blank).
let line = line.strip_suffix('\r').unwrap_or(line);
if line.contains('#') || line.is_empty() {
continue;
}
if line.starts_with("udp") {
if !curr.is_empty() {
fp_map.insert(count, curr);
curr = String::new();
}
capturing = true;
count += 1;
}
if capturing {
if !curr.is_empty() {
curr.push(' ');
}
curr.push_str(line);
}
}
let pb_linenr = ports_v(&fp_map);
let payb_linenr = payloads_v(&fp_map);
let map = port_payload_map(pb_linenr, payb_linenr);
generate_code(map);
}
/// Generates a file called Generated.rs and calls cargo fmt from the command line
///
/// # Arguments
///
/// * `port_payload_map` - A BTreeMap mapping port numbers to payload data
fn generate_code(port_payload_map: BTreeMap<Vec<u16>, Vec<u8>>) {
let dest_path = PathBuf::from("src/generated.rs");
let mut generated_code = String::new();
generated_code.push_str("use std::collections::BTreeMap;\n");
generated_code.push_str("use once_cell::sync::Lazy;\n\n");
generated_code.push_str("fn generated_data() -> BTreeMap<Vec<u16>, Vec<u8>> {\n");
generated_code.push_str(" let mut map = BTreeMap::new();\n");
for (ports, payloads) in port_payload_map {
generated_code.push_str(" map.insert(vec![");
generated_code.push_str(
&ports
.iter()
.map(|&p| p.to_string())
.collect::<Vec<_>>()
.join(","),
);
generated_code.push_str("], vec![");
generated_code.push_str(
&payloads
.iter()
.map(|&p| p.to_string())
.collect::<Vec<_>>()
.join(","),
);
generated_code.push_str("]);\n");
}
generated_code.push_str(" map\n");
generated_code.push_str("}\n\n");
generated_code.push_str(
"static PARSED_DATA: Lazy<BTreeMap<Vec<u16>, Vec<u8>>> = Lazy::new(generated_data);\n",
);
generated_code.push_str("pub fn get_parsed_data() -> &'static BTreeMap<Vec<u16>, Vec<u8>> {\n");
generated_code.push_str(" &PARSED_DATA\n");
generated_code.push_str("}\n");
fs::write(dest_path, generated_code).unwrap();
// format the generated code
Command::new("cargo")
.arg("fmt")
.arg("--all")
.output()
.expect("Failed to execute cargo fmt");
}
/// Creates a BTreeMap of line numbers mapped to a Vec<u16> of ports
///
/// # Arguments
///
/// * `fp_map` - A BTreeMap containing the parsed file data
///
/// # Returns
///
/// A BTreeMap where keys are line numbers and values are vectors of ports
fn ports_v(fp_map: &BTreeMap<i32, String>) -> BTreeMap<i32, Vec<u16>> {
let mut pb_linenr: BTreeMap<i32, Vec<u16>> = BTreeMap::new();
let mut port_list: Vec<u16> = Vec::new();
for (&line_nr, ports) in fp_map {
if ports.contains("udp ") {
let remain = &ports[4..];
let mut start = remain.split(' ');
let ports = start.next().unwrap();
let port_segments: Vec<&str> = ports.split(',').collect();
for segment in port_segments {
if segment.contains('-') {
let range: Vec<&str> = segment.trim().split('-').collect();
let start = range[0].parse::<u16>().unwrap();
let end = range[1].parse::<u16>().unwrap();
for port in start..end {
port_list.push(port);
}
} else if !segment.is_empty() {
match segment.parse::<u16>() {
Ok(port) => port_list.push(port),
Err(_) => println!("Error parsing port: {segment}"),
}
}
}
}
pb_linenr.insert(line_nr, port_list.clone());
port_list.clear();
}
pb_linenr
}
/// Parses out the Payloads into a BTreeMap of line numbers mapped to vectors of payload bytes
///
/// # Arguments
///
/// * `fp_map` - A BTreeMap containing the parsed file data
///
/// # Returns
///
/// A BTreeMap where keys are line numbers and values are vectors of payload bytes
fn payloads_v(fp_map: &BTreeMap<i32, String>) -> BTreeMap<i32, Vec<u8>> {
let mut payb_linenr: BTreeMap<i32, Vec<u8>> = BTreeMap::new();
for (&line_nr, data) in fp_map {
if data.contains('"') {
let start = data.find('"').expect("payload opening \" not found");
// Pass from the opening quote: the decoder pairs quotes itself
// to split multi-line segments.
payb_linenr.insert(line_nr, parser(data[start..].trim()));
}
}
payb_linenr
}
/// Converts a quoted Nmap payload block to bytes.
///
/// # Arguments
///
/// * `payload` - The raw block text starting at its first opening quote:
/// one or more `"..."` segments separated by whitespace
///
/// # Returns
///
/// The decoded probe bytes: segments are decoded with [`decode_segment`]
/// and concatenated with no separators, so multi-line probes reassemble
/// exactly.
fn parser(payload: &str) -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new();
let mut rest = payload.trim();
while let Some(open) = rest.find('"') {
let after_open = &rest[open + 1..];
match split_segment(after_open) {
Some((segment, remainder)) => {
decode_segment(segment, &mut bytes);
rest = remainder;
}
None => {
// Unterminated trailing quote: decode what's left literally.
decode_segment(after_open, &mut bytes);
break;
}
}
}
bytes
}
/// Splits off the first `"..."` segment: `text` must start just after an
/// opening quote. Escaped quotes (`\"`) do not terminate the segment.
///
/// Returns the segment body and the remainder after the closing quote.
fn split_segment(text: &str) -> Option<(&str, &str)> {
let mut escaped = false;
for (i, c) in text.char_indices() {
if escaped {
escaped = false;
continue;
}
if c == '\\' {
escaped = true;
continue;
}
if c == '"' {
return Some((&text[..i], &text[i + 1..]));
}
}
None
}
/// Decodes one quoted Nmap payload segment in C-style escape format:
/// `\xNN` becomes a byte, common single-char escapes (`\\`, `\"`,
/// `\0`, `\n`, `\r`, `\t`) decode to their value, and every other
/// character contributes its literal bytes.
///
/// Preserving literal text matters: several probes embed plain-text
/// protocol words (the SNMP `public` community string, NetBIOS names,
/// LDAP `objectClass`, SSDP headers). The previous hexdigits-only
/// decoding mangled them into wrong bytes, so agents never answered.
fn decode_segment(segment: &str, bytes: &mut Vec<u8>) {
let mut chars = segment.chars();
while let Some(c) = chars.next() {
if c != '\\' {
let mut buf = [0; 4];
bytes.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
continue;
}
match chars.next() {
Some('x') => {
let hi = chars.next().unwrap_or('0');
let lo = chars.next().unwrap_or('0');
let hex: String = [hi, lo].iter().collect();
bytes.push(u8::from_str_radix(&hex, 16).unwrap_or(0));
}
Some('0') => bytes.push(0),
Some('n') => bytes.push(b'\n'),
Some('r') => bytes.push(b'\r'),
Some('t') => bytes.push(b'\t'),
Some('\\') => bytes.push(b'\\'),
Some('"') => bytes.push(b'"'),
// Unknown escape: keep the character literally, matching Nmap,
// which passes unrecognized escapes through untouched.
Some(other) => {
let mut buf = [0; 4];
bytes.extend_from_slice(other.encode_utf8(&mut buf).as_bytes());
}
None => bytes.push(b'\\'),
}
}
}
/// Combines the ports BTreeMap and the Payloads BTreeMap
///
/// # Arguments
///
/// * `pb_linenr` - A BTreeMap mapping line numbers to vectors of ports
/// * `payb_linenr` - A BTreeMap mapping line numbers to vectors of payload bytes
///
/// # Returns
///
/// A BTreeMap mapping vectors of ports to vectors of payload bytes
fn port_payload_map(
pb_linenr: BTreeMap<i32, Vec<u16>>,
payb_linenr: BTreeMap<i32, Vec<u8>>,
) -> BTreeMap<Vec<u16>, Vec<u8>> {
let mut ppm_fin: BTreeMap<Vec<u16>, Vec<u8>> = BTreeMap::new();
for (port_linenr, ports) in pb_linenr {
for (pay_linenr, payloads) in &payb_linenr {
if pay_linenr == &port_linenr {
ppm_fin.insert(ports.to_vec(), payloads.to_vec());
}
}
}
ppm_fin
}
+4 -4
View File
@@ -1,5 +1,5 @@
# Dockerfile for RustScan development environment
# Provides a containerized setup with Rust and development tools
FROM rust
# Install nmap first.
RUN apt-get update -qy && apt-get install -qy nmap
# Then install rustfmt and clippy for cargo.
RUN rustup component add rustfmt clippy
# Install rustfmt for code formatting and clippy for linting.
RUN rustup component add rustfmt clippy
+7 -2
View File
@@ -54,10 +54,10 @@ You can now modify RustScan files with your favorite editor, once you want to co
root@container:/rustscan# cargo build
```
You are now ready to use RustScan:
You can check the command-line interface without starting a scan:
```bash
root@container:/rustscan# cargo run -- -b 2000 -t 5000 -a 127.0.0.1
root@container:/rustscan# cargo run -- --help
```
You can also format, lint with `clippy` and test the code with the following commands:
@@ -67,3 +67,8 @@ root@container:/rustscan# cargo fmt
root@container:/rustscan# cargo clippy
root@container:/rustscan# cargo test
```
Automated tests and benchmarks must run without network traffic or external script
execution. Keep coverage focused on parsing, configuration, port ordering, socket
address iteration, and timing helpers. Do not add tests that run TCP/UDP scans
(including localhost), resolve hostnames, or launch Nmap or other scripts.
+38
View File
@@ -0,0 +1,38 @@
# ARM support
This page covers which ARM targets RustScan's CI builds and tests, and how to build ARM binaries yourself.
## What CI covers
| Target | Built by | Tested by |
| --- | --- | --- |
| `aarch64-unknown-linux-gnu` (64-bit ARM Linux) | `build-nix` job in [build.yml](../.github/workflows/build.yml) | `Test Suite (ubuntu-24.04-arm)` in [test.yml](../.github/workflows/test.yml) |
| `armv7-unknown-linux-gnueabihf` (32-bit ARM Linux) | `build-nix` job in [build.yml](../.github/workflows/build.yml) | not tested in CI |
| `aarch64-apple-darwin` (Apple Silicon macOS) | `build-macos-aarch64` job in [build.yml](../.github/workflows/build.yml) | `Test Suite (macos-latest)` in [test.yml](../.github/workflows/test.yml) |
- **Builds** use [`houseabsolute/actions-rust-cross`](https://github.com/houseabsolute/actions-rust-cross). It cross-compiles the Linux ARM targets with [`cross`](https://github.com/cross-rs/cross) on x86_64 runners.
- **Tests** run natively on GitHub-hosted arm64 runners: `ubuntu-24.04-arm` for Linux, and `macos-latest`, which runs on Apple Silicon. They run the same `just test` recipe as every other platform.
## Building ARM binaries locally
The simplest option is `cross`, which runs the build in a container that already has the right toolchain and linker. It needs Docker or Podman.
```sh
cargo install cross
cross build --locked --release --target aarch64-unknown-linux-gnu
# or, for 32-bit ARM:
cross build --locked --release --target armv7-unknown-linux-gnueabihf
```
The binary ends up at `target/<target>/release/rustscan`.
On an ARM machine (for example a Raspberry Pi running a 64-bit OS, or an Apple Silicon Mac) you don't need `cross`; a normal native build works:
```sh
cargo build --locked --release
```
## Troubleshooting
- **`error[E0463]: can't find crate for std`, or linker errors:** you're cross-compiling with plain `cargo`. Either use `cross`, or install the target (`rustup target add <target>`) along with a matching cross linker.
- **`cross` can't pull its image:** check that Docker or Podman is running and that your user can reach it.
+4
View File
@@ -0,0 +1,4 @@
tags = ["core_approved", "example"]
ports = ["80", "443", "8080"]
developer = ["example", "https://example.org"]
directory = "fixtures/.rustscan_scripts"
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/perl
#tags = ["core_approved", "example",]
#developer = [ "example", "https://example.org" ]
#ports_separator = ","
#call_format = "perl {{script}} {{ip}} {{port}}"
# Sriptfile parser stops at the first blank line with parsing.
# This script will run itself as an argument with the system installed perl interpreter, ports will be concatenated with "," .
# Unused field: trigger_port = "80"
# get total arg passed to this script
my $total = $#ARGV + 1;
my $counter = 1;
# get script name
my $scriptname = $0;
print "Total args passed to $scriptname : $total\n";
# Use loop to print all args stored in an array called @ARGV
foreach my $a(@ARGV) {
print "Arg # $counter : $a\n";
$counter++;
}
-13
View File
@@ -1,13 +0,0 @@
#!/usr/bin/python3
#tags = ["core_approved", "example",]
#developer = [ "example", "https://example.org" ]
#trigger_port = "80"
#call_format = "python3 {{script}} {{ip}} {{port}}"
# Sriptfile parser stops at the first blank line with parsing.
# This script will run itself as an argument with the system installed python interpreter, only scanning port 80.
# Unused filed: ports_separator = ","
import sys
print('Python script ran with arguments', str(sys.argv))
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
#tags = ["core_approved", "example",]
#developer = [ "example", "https://example.org" ]
#ports_separator = ","
#call_format = "bash {{script}} {{ip}} {{port}}"
# Sriptfile parser stops at the first blank line with parsing.
# This script will run itself as an argument with the system installed bash interpreter, scanning all ports concatenated with "," .
# Unused filed: trigger_port = "80"
# print all arguments passed to the script
echo $@
+3 -5
View File
@@ -1,9 +1,7 @@
#!intentional_blank_line
Metadata-only test fixture; not executable.
#tags = ["core_approved", "example"]
#developer = [ "example", "https://example.org" ]
#ports_separator = ","
#call_format = "nmap -vvv -p {{port}} {{ip}}"
#call_format = "fixture {{ip}} {{port}}"
# Scriptfile parser stops at the first blank line with parsing.
# This script will run the system installed nmap, ports will be concatenated with "," .
# Unused field: trigger_port = "80"
The parser reads only the metadata above. This file contains no script body.
@@ -1,7 +1,7 @@
#!intentional_blank_line
Metadata-only test fixture; not executable.
#tags = "core_approved"
#developer = [ "example", "https://example.org" ]
#ports_separator = ","
#call_format = "nmap -vvv -p {{port}} {{ip}}"
#call_format = "fixture {{ip}} {{port}}"
# tags has to be an array, thus this file won't be parsed
# tags has to be an array, thus this file won't be parsed
View File
Whitespace-only changes.
-6
View File
@@ -1,6 +0,0 @@
127.0.0.1
google.com
example.com
66666666666666.666666666666666666.66666666666666666.6666666666
....
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.radiatorFixtures
+18
View File
@@ -0,0 +1,18 @@
test:
cargo test
cargo clippy -- --deny warnings
cargo clippy --tests -- --deny warnings
cargo fmt --check
cargo doc --workspace --all-features --no-deps --document-private-items
fix:
cargo fmt
cargo clippy --fix
# release only runs if test runs
release: test
git checkout master
git pull
cargo generate-lockfile
cargo publish
git push
+500
View File
@@ -0,0 +1,500 @@
# Nmap nmap payload database -*- mode: fundamental; -*-
# $Id$
#
# These payloads are sent with every host discovery or port scan probe
# by default. This database should only include payloads that are
# unlikely to crash services, trip IDS alerts, or change state on the
# server. The idea behind these is to evoke a response using a payload.
# Some of them are taken from nmap-service-probes.
#
# This collection of data is (C) 1996-2022 by Nmap Software LLC. It
# is distributed under the Nmap Public Source license as provided in
# the LICENSE file of the source distribution or at
# https://nmap.org/npsl/. Note that this free license does not allow
# incorporation of Nmap or its data files within proprietary
# software. We sell a separate Nmap OEM for that as described
# (including pricing) at https://nmap.org/npsl/.
#
# Each entry begins with a protocol (only "udp" is supported) followed
# by a comma-separated list of ports, followed by one or more quoted
# strings containing the payload. These elements may be broken across
# several lines. For future expansion, additional keywords may follow
# the payload data. Any data following one of these keywords must be on
# the same line as the keyword so that unknown keywords can be ignored
# by the parser. Currently this file contains some entries with the
# "source" keyword to specify a desired source port, but it is not
# honored by Nmap.
#
# Multiple payloads may be defined for a single protocol and port, in
# which case they will be all be sent concurrently. There is a limit
# of 255 payloads per port.
#
# Lines longer than 1024 characters will be ignored.
#
# Example:
# udp 1234 "payloaddatapayloaddata"
# "payloaddatapayloaddata"
# source 5678
# GenericLines. Use for the echo service.
udp 7 "\x0D\x0A\x0D\x0A"
# DNSStatusRequest
# Sent for TFTP (69) as well due to its ability to provoke an error response
udp 53,69,5353,26198 "\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# DNS VER
udp 53,5353,26198
"\x77\x77\x01\x00\x00"
"\x01\x00\x00\x00\x00\x00\x00\x07version\x04bind\x00\x00\x10\x00\x03"
# DHCP INFORM
udp 67
"\x01\x01\x06\x00"
"\x01\x23\x45\x67\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x35\xd4\xd8\x51\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x63\x82\x53\x63\x35\x01"
"\x08\xff"
# TFTP GET
udp 69 "\x00\x01r7tftp.txt\x00octet\x00"
# QUIC packet with unsupported version Q999
# Also found on 443, but need to check whether DTLS or QUIC is more prevalent
udp 80 "\r12345678Q999\x00"
# RPCCheck
udp 111
"\x72\xFE\x1D\x13\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x86\xA0"
"\x00\x01\x97\x7C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
# ONCRPC CALL
udp 111,2049,4045,32768-65535
"\x3e\xec\xe3\xca\x00\x00\x00\x00\x00\x00\x00\x02\x00"
"\xbc\x61\x4e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# NTPRequest
udp 123
"\xE3\x00\x04\xFA\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\xC5\x4F\x23\x4B\x71\xB1\x52\xF3"
# NTP REQ
udp 123
"\xd9\x00\x0a\xfa\x00\x00\x00"
"\x00\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6"
"\xf1\x5e\xdb\x78\x00\x00\x00"
# DCERPC CALL
udp 135,1025-1199
"\x05\x00\x0b\x03\x10\x00\x00\x00\x48\x00"
"\x00\x00\x01\x00\x00\x00\xb8\x10\xb8\x10\x00\x00\x00\x00\x01\x00\x00"
"\x00\x00\x00\x01\x00\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67"
"\x89\xab\xcd\xef\xe7\x03\x00\x00\xfe\xdc\xba\x98\x76\x54\x32\x10\x01"
"\x23\x45\x67\x89\xab\xcd\xef\xe7\x03\x00\x00"
# NBTStat
udp 137
"\x80\xF0\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00"
"\x20CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x21\x00\x01"
# CIFS NS NAME QUERY UC
udp 137
"\x01\x91\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
"\x20CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x21\x00\x01"
# CIFS NS NAME QUERY BC
udp 137
"\x01\x91\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00"
"\x20CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x21\x00\x01"
# SNMPv3GetRequest
udp 161,260,3401
"\x30\x3A\x02\x01\x03\x30\x0F\x02\x02\x4A\x69\x02\x03\x00\xFF\xE3"
"\x04\x01\x04\x02\x01\x03\x04\x10\x30\x0E\x04\x00\x02\x01\x00\x02"
"\x01\x00\x04\x00\x04\x00\x04\x00\x30\x12\x04\x00\x04\x00\xA0\x0C"
"\x02\x02\x37\xF0\x02\x01\x00\x02\x01\x00\x30\x00"
# SNMP PUBLIC WALK
udp 161,260,3401
"\x30\x1f\x02\x01\x00\x04\x06public\xa1\x12\x02\x01\x00\x02"
"\x01\x00\x02\x01\x00\x30\x07\x30\x05\x06\x01\x00\x05\x00"
# Sqlping - disabled because it trips a Snort rule with SID 2049
# ("MS-SQL ping attempt").
# udp 1434 "\x02"
# xdmcp - X Display Manager Control Protocol. Version 1, packet type
# Query (2), no authorization names. We expect a Willing or Unwilling
# packet in reply.
# http://cgit.freedesktop.org/xorg/doc/xorg-docs/plain/hardcopy/XDMCP/xdmcp.PS.gz
udp 177 "\x00\x01\x00\x02\x00\x01\x00"
# Connectionless LDAP - used by Microsoft Active Directory
udp 389
"\x30\x84\x00\x00\x00\x2d\x02\x01\x07\x63\x84\x00\x00\x00\x24\x04\x00"
"\x0a\x01\x00\x0a\x01\x00\x02\x01\x00\x02\x01\x64\x01\x01\x00\x87\x0b"
"objectClass0\x84\x00\x00\x00\x00"
# svrloc
udp 427
"\x02\x01\x00\x006 \x00\x00\x00\x00\x00\x01\x00\x02en\x00\x00\x00\x15"
"service:service-agent\x00\x07default\x00\x00\x00\x00"
# DTLS
udp 443,853,3391,4433,4740,5349,5684,5868,6514,6636,8232,10161,10162,12346,12446,12546,12646,12746,12846,12946,13046
# DTLS 1.0, length 52
"\x16\xfe\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x36"
# ClientHello, length 40, sequence 0, offset 0
"\x01\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00\x2a"
# DTLS 1.2
"\xfe\xfd"
# Random
"\x00\x00\x00\x00\x7c\x77\x40\x1e\x8a\xc8\x22\xa0\xa0\x18\xff\x93"
"\x08\xca\xac\x0a\x64\x2f\xc9\x22\x64\xbc\x08\xa8\x16\x89\x19\x3f"
# Session id length 0, cookie length 0
"\x00\x00"
# Cipher suites, mandatory TLS_RSA_WITH_AES_128_CBC_SHA
"\x00\x02\x00\x2f"
# Compressors (NULL)
"\x01\x00"
# Internet Key Exchange version 1, phase 1 Main Mode. We offer every
# combination of (DES, 3DES) and (MD5, SHA) in the hope that one of them will
# be acceptable. Because we use a fixed cookie, we set the association lifetime
# to 1 second to reduce the chance that repeated probes will look like
# retransmissions (and therefore not get a response). This payload comes from
# ike-scan --lifetime 1 --cookie 0011223344556677 --trans=5,2,1,2 --trans=5,1,1,2 --trans=1,2,1,2 --trans=1,1,1,2
# We expect another phase 1 message in response. This payload works better with
# a source port of 500 or a randomized initiator cookie.
udp 500
# Initiator cookie 0x0011223344556677, responder cookie 0x0000000000000000.
"\x00\x11\x22\x33\x44\x55\x66\x77\x00\x00\x00\x00\x00\x00\x00\x00"
# Version 1, Main Mode, flags 0x00, message ID 0x00000000, length 192.
"\x01\x10\x02\x00\x00\x00\x00\x00\x00\x00\x00\xC0"
# Security Association payload, length 164, IPSEC, IDENTITY.
"\x00\x00\x00\xA4\x00\x00\x00\x01\x00\x00\x00\x01"
# Proposal 1, length 152, ISAKMP, 4 transforms.
"\x00\x00\x00\x98\x01\x01\x00\x04"
# Transform 1, 3DES-CBC, SHA, PSK, group 2.
"\x03\x00\x00\x24\x01\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02"
"\x80\x03\x00\x01\x80\x04\x00\x02"
"\x80\x0B\x00\x01\x00\x0C\x00\x04\x00\x00\x00\x01"
# Transform 2, 3DES-CBC, MD5, PSK, group 2.
"\x03\x00\x00\x24\x02\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x01"
"\x80\x03\x00\x01\x80\x04\x00\x02"
"\x80\x0B\x00\x01\x00\x0C\x00\x04\x00\x00\x00\x01"
# Transform 3, DES-CBC, SHA, PSK, group 2.
"\x03\x00\x00\x24\x03\x01\x00\x00\x80\x01\x00\x01\x80\x02\x00\x02"
"\x80\x03\x00\x01\x80\x04\x00\x02"
"\x80\x0B\x00\x01\x00\x0C\x00\x04\x00\x00\x00\x01"
# Transform 4, DES-CBC, MD5, PSK, group 2.
"\x00\x00\x00\x24\x04\x01\x00\x00\x80\x01\x00\x01\x80\x02\x00\x01"
"\x80\x03\x00\x01\x80\x04\x00\x02"
"\x80\x0B\x00\x01\x00\x0C\x00\x04\x00\x00\x00\x01"
# IPSEC START
udp 500,4500
"\x31\x27\xfc"
"\xb0\x38\x10\x9e\x89\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x02\x00"
"\x00\x00\x00\x00\x00\x00\x00\xcc\x0d\x00\x00\x5c\x00\x00\x00\x01\x00"
"\x00\x00\x01\x00\x00\x00\x50\x01\x01\x00\x02\x03\x00\x00\x24\x01\x01"
"\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02\x80\x04\x00\x02\x80\x03\x00"
"\x03\x80\x0b\x00\x01\x00\x0c\x00\x04\x00\x00\x0e\x10\x00\x00\x00\x24"
"\x02\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x01\x80\x04\x00\x02\x80"
"\x03\x00\x03\x80\x0b\x00\x01\x00\x0c\x00\x04\x00\x00\x0e\x10\x0d\x00"
"\x00\x18\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4"
"\x61\x00\x00\x00\x04\x0d\x00\x00\x14\x40\x48\xb7\xd5\x6e\xbc\xe8\x85"
"\x25\xe7\xde\x7f\x00\xd6\xc2\xd3\x0d\x00\x00\x14\x90\xcb\x80\x91\x3e"
"\xbb\x69\x6e\x08\x63\x81\xb5\xec\x42\x7b\x1f\x00\x00\x00\x14\x26\x24"
"\x4d\x38\xed\xdb\x61\xb3\x17\x2a\x36\xe3\xd0\xcf\xb8\x19"
# Routing Information Protocol version 1. Special-case request for the entire
# routing table (address family 0, address 0.0.0.0, metric 16). RFC 1058,
# section 3.4.1.
udp 520
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x10"
# RMCP ASF ping
udp 623
"\x06\x00\xff\x06" # RMCP version 6, sequence 0xff, normal RMCP class ASF
"\x00\x00\x11\xbe" # IAN ASF code 4542
"\x80\x00\x00\x00" # payload-less ASF presence ping
# IPMI
# RMCP Get Channel Auth Capabilities
udp 623
"\x06\x00\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x20\x18"
"\xc8\x81\x00\x38\x8e\x04\xb5"
# serialnumberd. This service runs on Mac OS X Server. This probe
# requests the serial number of another server. In response we expect a
# packet starting with "SNRESPS:", followed by some data whose purpose
# is not known.
udp 626 "SNQUERY: 127.0.0.1:AAAAAA:xsvr"
# OpenVPN P_CONTROL_HARD_RESET_CLIENT_V2
# Byte 0; 0x38 opcode
# Byte 1-8: Session ID, random
# Byte 9: Message packet-id array length (0)
# Byte 10-13: Message packet-id (0)
udp 1194 "8d\xc1x\x01\xb8\x9b\xcb\x8f\0\0\0\0\0"
# OpenVPN when in PKI mode and without the "HMAC Firewall" setting enabled
# (tls-auth) should respond to the following probe, which is
# 0x38<8 random bytes><4 null bytes>
udp 1194
"\x38\x01\x02\x03\x04\x05\x06\x07\x08\x00\x00\x00\x00"
# Citrix MetaFrame application browser service
# Original idea from http://sh0dan.org/oldfiles/hackingcitrix.html
# Payload contents copied from Wireshark capture of Citrix Program
# Neighborhood client application. The application uses this payload to
# locate Citrix servers on the local network. Response to this probe is
# a 48 byte UDP payload as shown here:
#
# 0000 30 00 02 31 02 fd a8 e3 02 00 06 44 c0 a8 80 55
# 0010 00 00 00 00 00 00 00 00 00 00 00 00 02 00 06 44
# 0020 c0 a8 80 56 00 00 00 00 00 00 00 00 00 00 00 00
#
# The first 12 bytes appear to be the same in all responses.
#
# Bytes 0x00 appears to be a packet length field
# Bytes 0x0C - 0x0F are the IP address of the server
# Bytes 0x10 - 0x13 may vary, 0x14 - 0x1F do not appear to
# Bytes 0x20 - 0x23 are the IP address of the primary system in a server farm
# configuration
# Bytes 0x24 - 0x27 can vary, 0x28 - 0x2F do not appear to
udp 1604
"\x1e\x00\x01\x30\x02\xfd\xa8\xe3\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# RADIUS Access-Request. This is a degenerate packet with no username or
# password; we expect an Access-Reject in response. The Identifier and Request
# Authenticator are both 0. It was generated by running
# echo 'User-Password = ""' | radclient <ip> auth ""
# and then manually stripping out the password.
#
# Section 2 of the RFC says "A request from a client for which the
# RADIUS server does not have a shared secret MUST be silently
# discarded." So this payload only works when the server is configured
# (or misconfigured) to know the scanning machine as a client.
#
# RFC 2865: "The early deployment of RADIUS was done using UDP port
# number 1645, which conflicts with the "datametrics" service. The
# officially assigned port number for RADIUS is 1812.
udp 1645,1812
"\x01\x00\x00\x14"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# L2TP ICRQ
udp 1701
"\xc8\x02"
"\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x00"
"\x01\x80\x08\x00\x00\x00\x02\x01\x00\x80\x0e\x00\x00\x00\x07"
"nxp-scan\x80\x0a\x00\x00\x00\x03\x00\x00\x00\x03\x80"
"\x08\x00\x00\x00\x09\x00\x00"
# UPNP MSEARCH
udp 1900
"M-SEARCH * HTTP/1.1\r\nHost: 239.255.255.250:1900\r\n"
"Man: \"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n"
# NFS version 2, RFC 1831. XID 0x00000000, program 100003 (NFS), procedure
# NFSPROC_NULL (does nothing, see section 2.2.1), null authentication (see
# section 9.1).
udp 2049
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x86\xA3"
"\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
# GPRS Tunneling Protocol (GTP)
udp 2123,2152
# GTPv1, protocol 1
"\x32"
# EchoRequest
"\x01"
# message length
"\x00\x04"
# Tunnel endpoint identifier
"\x00\x00\x42\x00"
# sequence number
"\x13\x37"
# N-PDU number
"\x00"
# next extension header type
"\x00"
# GPRS Tunneling Protocol (GTP) "prime" v2
# This same packet can be used for GTP v2 on ports 2123 and 2152 if you change
# the first byte from \x4e to \x40
udp 3386
# GTP'v2
"\x4e"
# EchoRequest
"\x01"
# message length
"\x00\x04"
# sequence number
"\xde\xfe\xc8\x00"
# Freelancer game server status query
# http://sourceforge.net/projects/gameq/
# (relevant files: games.ini, packets.ini, freelancer.php)
udp 2302 "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21"
# Apple Remote Desktop (ARD)
udp 3283 "\0\x14\0\x01\x03"
# STUN Binding request, see RFC 5389 Section 6
# message type = 0x001, Binding (see Section 18.1)
# message length = 0
# magic cookie = 0x2112a442
# transaction ID = "\x00"*12
udp 3478 "\x00\x01\x00\x00\x21\x12\xa4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# Sun Service Tag Discovery protocol (stdiscover)
# http://arc.opensolaris.org/caselog/PSARC/2006/638/stdiscover_protocolv2.pdf
# Would work better with a varying cookie; the second and later sends of this
# probe will be interpreted as resends by the server and will be ignored.
udp 6481 "[PROBE] 0000"
# NAT-PMP external IP address request. See section 3.2 of
# http://files.dns-sd.org/draft-cheshire-nat-pmp.txt.
udp 5351 "\x00\x00"
# DNS Service Discovery (DNS-SD) service query, as used in Zeroconf.
# Transaction ID 0x0000, flags 0x0000, 1 question: PTR query for
# _services._dns-sd._udp.local. If the remote host supports DNS-SD it will send
# back a list of all its services. This is the same as a packet capture of
# dns-sd -B _services._dns-sd._udp .
# See section 9 of
# http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt.
# This first probe is a QU probe, meaning a unicast response is desired
udp 5353
"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
"\x09_services\x07_dns-sd\x04_udp\x05local\x00\x00\x0C\x80\x01"
# This second probe is a QM probe, meaning a unicast response is desired
udp 5353
"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
"\x09_services\x07_dns-sd\x04_udp\x05local\x00\x00\x0C\x00\x01"
# PCANY STATUS
udp 5632 "ST"
# CoAP GET .well-known/core
udp 5683 "@\x01\x01\xce\xbb.well-known\x04core"
# UT2K PING
udp 7777 "None\x00"
# Ubiquiti Discovery Service - v1
udp 10001 "\x01\x00\x00\x00"
# Amanda backup service noop request. I think that this does nothing on the
# server but only asks it to send back its feature list. In reply we expect an
# ACK or (more likely) an ERROR. I couldn't find good online documentation of
# the Amanda network protocol. There is parsing code in the Amanda source at
# common-src/security-util.c. This is based on a packet capture of
# amcheck <config> <host>
udp 10080
"Amanda 2.6 REQ HANDLE 000-00000000 SEQ 0\n"
"SERVICE noop\n"
# VxWorks Wind River Debugger
udp 17185
# Random XID
"\x00\x00\x00\x00"
# RPC version 2 procedure call
"\x00\x00\x00\x00\x00\x00\x00\x02"
# WDB version 1
"\x55\x55\x55\x55\x00\x00\x00\x01"
# WDB_TARGET_PING
"\x00\x00\x00\x00"
# RPC Auth NULL
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# Checksum
"\xff\xff\x55\x13"
# WDB wrapper (length and sequence number)
"\x00\x00\x00\x30\x00\x00\x00\x01"
# Empty data?
"\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00"
# VXWORKS DEBUG (alternative?)
udp 17185
"\x72\x37\x72\x37\x00\x00\x00"
"\x00\x00\x00\x00\x02\x55\x55\x55\x55\x00\x00\x00\x01\x00\x00\x00\x01"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff"
"\xff\x55\x10\x00\x00\x00\x3c\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00"
"\x00\x00\x00\x00\x00\x00"
# Quake 2 and Quake 3 game servers (and servers of derived games like Nexuiz).
# Gets game information from the server (see probe responses in
# nmap-service-probes). These services typically run on a base port or a
# few numbers higher.
# Quake 2. Typical ports: 27910-27914.
udp 27910-27914 "\xff\xff\xff\xffstatus"
# Quake 3. Typical ports:
# 26000-26004: Nexuiz
# 27960-27964: Various games
# 30720-30724: Tremulous
# 44400: Warsow
udp 26000-26004,27960-27964,30720-30724,44400 "\xff\xff\xff\xffgetstatus"
# Murmur 1.2.X (Mumble server)
# UDP ping. "abcdefgh" is an identifier. See
# http://mumble.sourceforge.net/Protocol.
udp 64738 "\x00\x00\x00\x00abcdefgh"
# Ventrilo 2.1.2+
# UDP general status request (encrypted).
# See http://aluigi.altervista.org/papers.htm#ventrilo
udp 3784
"\x01\xe7\xe5\x75\x31\xa3\x17\x0b\x21\xcf\xbf\x2b\x99\x4e\xdd\x19\xac\xde\x08\x5f\x8b\x24\x0a\x11\x19\xb6\x73\x6f\xad\x28\x13\xd2\x0a\xb9\x12\x75"
# Kademlia (kad) as used by various P2P applications. Send a Kademlia ping
# 4665, 4666, 4672, 6429: eDonkey/eMule and variants
udp 4665,4666,4672,6429 "\xE4\x60"
# TeamSpeak 2
# UDP login request
# See http://wiki.wireshark.org/TeamSpeak2
udp 8767
"\xf4\xbe\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x002x\xba\x85\tTeamSpeak\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\nWindows XP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00 \x00<\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08nickname\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
# TS3INIT1
udp 9987
"TS3INIT1\x00\x65\x00\x00\x88\x0a\x39\x7b\x0f\x00\x5b\x55\x72\xef\xdc\x78\x32\x6b\x00\x00\x00\x00\x00\x00\x00\x00"
# TeamSpeak 3
# UDP login request (encrypted)
# http://seclists.org/nmap-dev/2013/q3/72
udp 9987
"\x05\xca\x7f\x16\x9c\x11\xf9\x89\x00\x00\x00\x00\x02\x9d\x74\x8b\x45\xaa\x7b\xef\xb9\x9e\xfe\xad\x08\x19\xba\xcf\x41\xe0\x16\xa2\x32\x6c\xf3\xcf\xf4\x8e\x3c\x44\x83\xc8\x8d\x51\x45\x6f\x90\x95\x23\x3e\x00\x97\x2b\x1c\x71\xb2\x4e\xc0\x61\xf1\xd7\x6f\xc5\x7e\xf6\x48\x52\xbf\x82\x6a\xa2\x3b\x65\xaa\x18\x7a\x17\x38\xc3\x81\x27\xc3\x47\xfc\xa7\x35\xba\xfc\x0f\x9d\x9d\x72\x24\x9d\xfc\x02\x17\x6d\x6b\xb1\x2d\x72\xc6\xe3\x17\x1c\x95\xd9\x69\x99\x57\xce\xdd\xdf\x05\xdc\x03\x94\x56\x04\x3a\x14\xe5\xad\x9a\x2b\x14\x30\x3a\x23\xa3\x25\xad\xe8\xe6\x39\x8a\x85\x2a\xc6\xdf\xe5\x5d\x2d\xa0\x2f\x5d\x9c\xd7\x2b\x24\xfb\xb0\x9c\xc2\xba\x89\xb4\x1b\x17\xa2\xb6"
# Memcached
# version request (shorter response than stats)
# https://github.com/memcached/memcached/blob/master/doc/protocol.txt
udp 11211
"\0\x01\0\0\0\x01\0\0version\r\n"
# Steam, typically using a port in 27015-27030. Send a "Source Engine query"
udp 27015-27030
"\xff\xff\xff\xffTSource Engine Query\x00"
# TRIN00 UNIX PING
udp 27444 "png l44adsl"
# BO PING
udp 31337
"\xce\x63\xd1\xd2\x16\xe7\x13\xcf\x38"
"\xa5\xa5\x86\xb2\x75\x4b\x99\xaa\x32\x58"
# TRIN00 WIN PING
udp 34555 "png []..Ks"
# Beckhoff ADS discovery request
# https://github.com/ONE75/adsclient/blob/master/src/AdsClient.Finder/DeviceFinder.cs#L49-L64
udp 48899
"\x03\x66\x14\x71\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x10\x27\x00\x00\x00\x00"
Executable
+1
View File
@@ -0,0 +1 @@
cargo publish && cargo build --release && git add Cargo.lock && git commit -m 'Updating lock file' && gpsup
-20
View File
@@ -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"]
-17
View File
@@ -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 \;
-8
View File
@@ -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
+471
View File
@@ -0,0 +1,471 @@
//! Provides functions to parse input IP addresses, CIDRs or files.
use std::collections::BTreeSet;
use std::fs::{self, File};
use std::io::{prelude::*, BufReader};
use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
use std::path::Path;
use std::str::FromStr;
use cidr_utils::cidr::{IpCidr, IpInet};
use hickory_resolver::{
config::{NameServerConfig, Protocol, ResolverConfig, ResolverOpts},
Resolver,
};
use log::debug;
use crate::input::Opts;
use crate::warning;
/// Parses the string(s) into IP addresses.
///
/// Goes through all possible IP inputs (files or via argparsing).
///
/// ```rust
/// # use rustscan::input::Opts;
/// # use rustscan::address::parse_addresses;
/// let mut opts = Opts::default();
/// opts.addresses = vec!["192.168.0.0/30".to_owned()];
///
/// let ips = parse_addresses(&opts);
/// ```
///
/// Finally, any duplicates are removed to avoid excessive scans.
pub fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
let mut ips: Vec<IpAddr> = Vec::new();
let mut unresolved_addresses: Vec<&str> = Vec::new();
let backup_resolver = get_resolver(&input.resolver);
for address in &input.addresses {
let parsed_ips = parse_address(address, &backup_resolver);
if !parsed_ips.is_empty() {
ips.extend(parsed_ips);
} else {
unresolved_addresses.push(address);
}
}
// If we got to this point this can only be a file path or the wrong input.
for file_path in unresolved_addresses {
let file_path = Path::new(file_path);
if !file_path.is_file() {
warning!(
format!("Host {file_path:?} could not be resolved."),
input.greppable,
input.accessible
);
continue;
}
if let Ok(x) = read_ips_from_file(file_path, &backup_resolver) {
ips.extend(x);
} else {
warning!(
format!("Host {file_path:?} could not be resolved."),
input.greppable,
input.accessible
);
}
}
let excluded_cidrs = parse_excluded_networks(&input.exclude_addresses, &backup_resolver);
// Remove duplicated/excluded IPs.
let mut seen = BTreeSet::new();
ips.retain(|ip| seen.insert(*ip) && !excluded_cidrs.iter().any(|cidr| cidr.contains(ip)));
ips
}
/// 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 every time you have a possible IP-or-host.
///
/// If the address is a domain, we can self-resolve the domain locally
/// or resolve it by dns resolver list.
///
/// ```rust
/// # use rustscan::address::parse_address;
/// # use hickory_resolver::Resolver;
/// let ips = parse_address("127.0.0.1", &Resolver::default().unwrap());
/// ```
pub fn parse_address(address: &str, resolver: &Resolver) -> Vec<IpAddr> {
if let Ok(addr) = IpAddr::from_str(address) {
// `address` is an IP string
vec![addr]
} else if let Ok(net_addr) = IpInet::from_str(address) {
// `address` is a CIDR string
net_addr.network().into_iter().addresses().collect()
} else {
// `address` is a hostname or DNS name
// attempt default DNS lookup
match format!("{address}:80").to_socket_addrs() {
Ok(mut iter) => vec![iter.next().unwrap().ip()],
// default lookup didn't work, so try again with the dedicated resolver
Err(_) => resolve_ips_from_host(address, resolver),
}
}
}
/// 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<IpAddr> = Vec::new();
if let Ok(addrs) = source.to_socket_addrs() {
for ip in addrs {
ips.push(ip.ip());
}
} else if let Ok(addrs) = backup_resolver.lookup_ip(source) {
ips.extend(addrs.iter());
}
ips
}
/// Parses excluded networks from a list of addresses.
///
/// This function handles three types of inputs:
/// 1. CIDR notation (e.g. "192.168.0.0/24")
/// 2. Single IP addresses (e.g. "192.168.0.1")
/// 3. Hostnames that need to be resolved (e.g. "example.com")
///
/// ```rust
/// # use rustscan::address::parse_excluded_networks;
/// # use hickory_resolver::Resolver;
/// let resolver = Resolver::default().unwrap();
/// let excluded = parse_excluded_networks(&Some(vec!["192.168.0.0/24".to_owned()]), &resolver);
/// ```
pub fn parse_excluded_networks(
exclude_addresses: &Option<Vec<String>>,
resolver: &Resolver,
) -> Vec<IpCidr> {
exclude_addresses
.iter()
.flatten()
.flat_map(|addr| parse_single_excluded_address(addr, resolver))
.collect()
}
/// Parses a single address into an IpCidr, handling CIDR notation, IP addresses, and hostnames.
fn parse_single_excluded_address(addr: &str, resolver: &Resolver) -> Vec<IpCidr> {
if let Ok(cidr) = IpCidr::from_str(addr) {
return vec![cidr];
}
if let Ok(ip) = IpAddr::from_str(addr) {
return vec![IpCidr::new_host(ip)];
}
resolve_ips_from_host(addr, resolver)
.into_iter()
.map(IpCidr::new_host)
.collect()
}
/// Derive a DNS resolver.
///
/// 1. if the `resolver` parameter has been set:
/// 1. assume the parameter is a path and attempt to read IPs.
/// 2. parse the input as a comma-separated list of IPs.
/// 2. if `resolver` is not set:
/// 1. attempt to derive a resolver from the system config. (e.g.
/// `/etc/resolv.conf` on *nix).
/// 2. finally, build a CloudFlare-based resolver (default
/// behaviour).
fn get_resolver(resolver: &Option<String>) -> Resolver {
match resolver {
Some(r) => {
let mut config = ResolverConfig::new();
let resolver_ips = match read_resolver_from_file(r) {
Ok(ips) => ips,
Err(_) => r
.split(',')
.filter_map(|r| IpAddr::from_str(r).ok())
.collect::<Vec<_>>(),
};
for ip in resolver_ips {
config.add_name_server(NameServerConfig::new(
SocketAddr::new(ip, 53),
Protocol::Udp,
));
}
Resolver::new(config, resolver_opts()).unwrap()
}
None => match system_resolver() {
Ok(resolver) => resolver,
Err(_) => Resolver::new(ResolverConfig::cloudflare_tls(), resolver_opts()).unwrap(),
},
}
}
/// `true` on Windows when the `SystemRoot` environment variable is unset.
///
/// hickory-resolver locates the hosts file via
/// `std::env::var_os("SystemRoot").expect(...)`, which panics when the
/// variable is missing. Processes spawned with a minimal environment
/// (services, scheduled tasks, WMI) can lack `SystemRoot`, and the panic is
/// fatal under this crate's `panic = "abort"` release profile.
fn windows_system_root_missing() -> bool {
cfg!(windows) && std::env::var_os("SystemRoot").is_none()
}
/// Derives a resolver from the system configuration, e.g. `/etc/resolv.conf`
/// on *nix or the registry on Windows.
///
/// Returns an error without touching the system configuration when doing so
/// would panic inside hickory-resolver (see [`windows_system_root_missing`]).
fn system_resolver() -> std::io::Result<Resolver> {
if windows_system_root_missing() {
debug!("SystemRoot is not set; skipping system resolver configuration");
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"SystemRoot environment variable is not set",
));
}
Resolver::from_system_conf()
}
/// Resolver options that are safe to use in the current environment.
///
/// hickory-resolver eagerly loads the hosts file when `use_hosts_file` is set
/// (the default), which hits the same missing-`SystemRoot` panic described in
/// [`windows_system_root_missing`]; disable it in that case.
fn resolver_opts() -> ResolverOpts {
let mut opts = ResolverOpts::default();
if windows_system_root_missing() {
opts.use_hosts_file = false;
}
opts
}
/// Parses and input file of IPs for use in DNS resolution.
fn read_resolver_from_file(path: &str) -> Result<Vec<IpAddr>, std::io::Error> {
let ips = fs::read_to_string(path)?
.lines()
.filter_map(|line| IpAddr::from_str(line.trim()).ok())
.collect();
Ok(ips)
}
#[cfg(not(tarpaulin_include))]
/// Parses an input file of IPs and uses those
fn read_ips_from_file(
ips: &std::path::Path,
backup_resolver: &Resolver,
) -> Result<Vec<IpAddr>, std::io::Error> {
let file = File::open(ips)?;
let reader = BufReader::new(file);
let mut ips: Vec<IpAddr> = Vec::new();
for address_line in reader.lines() {
if let Ok(address) = address_line {
ips.extend(parse_address(&address, backup_resolver));
} else {
debug!("Line in file is not valid");
}
}
Ok(ips)
}
#[cfg(test)]
mod tests {
use super::{parse_addresses, Opts};
use std::net::{IpAddr, Ipv4Addr};
#[test]
fn parse_correct_addresses() {
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!(
ips,
[
Ipv4Addr::new(127, 0, 0, 1),
Ipv4Addr::new(192, 168, 0, 0),
Ipv4Addr::new(192, 168, 0, 1),
Ipv4Addr::new(192, 168, 0, 2),
Ipv4Addr::new(192, 168, 0, 3)
]
);
}
#[test]
fn parse_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_duplicate_cidrs() {
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);
assert_eq!(ips.len(), 2_048);
}
#[test]
fn parse_overspecific_cidr() {
// a canonical CIDR string has 0 in all host bits, but we want to treat any CIDR-like string as CIDR
let opts = Opts {
addresses: vec!["192.128.1.1/24".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 256);
}
#[test]
fn parse_non_canonical_cidr_mid_block() {
// 192.168.1.13/29: .13 = 0000 1101, mask clears last 3 bits → .8 = 0000 1000
// network is 192.168.1.8/29, spanning .8 through .15
let opts = Opts {
addresses: vec!["192.168.1.13/29".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
ips,
[
Ipv4Addr::new(192, 168, 1, 8),
Ipv4Addr::new(192, 168, 1, 9),
Ipv4Addr::new(192, 168, 1, 10),
Ipv4Addr::new(192, 168, 1, 11),
Ipv4Addr::new(192, 168, 1, 12),
Ipv4Addr::new(192, 168, 1, 13),
Ipv4Addr::new(192, 168, 1, 14),
Ipv4Addr::new(192, 168, 1, 15),
]
);
}
#[test]
fn parse_non_canonical_cidr_last_in_block() {
// 192.168.1.15/29: last address in the block, should still resolve to same .8–.15 network
let opts = Opts {
addresses: vec!["192.168.1.15/29".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
ips,
[
Ipv4Addr::new(192, 168, 1, 8),
Ipv4Addr::new(192, 168, 1, 9),
Ipv4Addr::new(192, 168, 1, 10),
Ipv4Addr::new(192, 168, 1, 11),
Ipv4Addr::new(192, 168, 1, 12),
Ipv4Addr::new(192, 168, 1, 13),
Ipv4Addr::new(192, 168, 1, 14),
Ipv4Addr::new(192, 168, 1, 15),
]
);
}
#[test]
fn parse_non_canonical_cidr_crosses_third_octet() {
// 192.168.1.5/23: host bits span into the third octet
// .1.5 in 23-bit context → network is 192.168.0.0/23, spanning .0.0 through .1.255 (512 addresses)
let opts = Opts {
addresses: vec!["192.168.1.5/23".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
ips.first(),
Some(&IpAddr::V4(Ipv4Addr::new(192, 168, 0, 0)))
);
assert_eq!(
ips.last(),
Some(&IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255)))
);
assert_eq!(ips.len(), 512);
}
#[test]
fn parse_non_canonical_cidr_slash30() {
// 10.0.0.7/30: .7 = 0000 0111, mask clears last 2 bits → .4 = 0000 0100
// network is 10.0.0.4/30, spanning .4 through .7
let opts = Opts {
addresses: vec!["10.0.0.7/30".to_owned()],
..Default::default()
};
let ips = parse_addresses(&opts);
assert_eq!(
ips,
[
Ipv4Addr::new(10, 0, 0, 4),
Ipv4Addr::new(10, 0, 0, 5),
Ipv4Addr::new(10, 0, 0, 6),
Ipv4Addr::new(10, 0, 0, 7),
]
);
}
}
+14 -11
View File
@@ -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;
@@ -37,13 +42,11 @@ impl Benchmark {
let mut summary = String::from("\nRustScan Benchmark Summary");
for timer in &self.named_timers {
if timer.start.is_some() && timer.end.is_some() {
let runtime_secs = timer
.end
.unwrap()
.saturating_duration_since(timer.start.unwrap())
.as_secs_f32();
summary.push_str(&format!("\n{0: <10} | {1: <10}s", timer.name, runtime_secs))
if let Some(start) = timer.start {
if let Some(end) = timer.end {
let runtime_secs = end.saturating_duration_since(start).as_secs_f32();
summary.push_str(&format!("\n{0: <10} | {1: <10}s", timer.name, runtime_secs));
}
}
}
summary
@@ -85,5 +88,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"));
}
+609 -132
View File
@@ -1,163 +1,311 @@
//! Provides a means to read, parse and hold configuration options for scans.
use clap::parser::ValueSource;
use clap::{CommandFactory, FromArgMatches, Parser, ValueEnum};
use serde_derive::Deserialize;
use std::collections::HashMap;
use std::collections::HashSet;
use std::ffi::OsString;
use std::fs;
use structopt::{clap::arg_enum, StructOpt};
use std::path::PathBuf;
const LOWEST_PORT_NUMBER: u16 = 1;
const TOP_PORT_NUMBER: u16 = 65535;
arg_enum! {
/// Represents the strategy in which the port scanning will run.
/// - Serial will run from start to end, for example 1 to 1_000.
/// - Random will randomize the order in which ports will be scanned.
#[derive(Deserialize, Debug, StructOpt, Clone, Copy, PartialEq)]
pub enum ScanOrder {
Serial,
Random,
}
/// 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, ValueEnum, Clone, Copy, PartialEq, Eq)]
pub enum ScanOrder {
Serial,
Random,
}
arg_enum! {
/// Represents the scripts variant.
/// - 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)]
pub enum ScriptsRequired {
None,
Default,
Custom,
}
/// Represents the scripts variant.
/// - 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, ValueEnum, Clone, PartialEq, Eq, Copy)]
pub enum ScriptsRequired {
None,
Default,
Custom,
}
/// Represents the range of ports to be scanned.
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct PortRange {
pub start: u16,
pub end: u16,
/// The port ranges to scan, as inclusive `(start, end)` pairs.
///
/// Parsed from `-r/--range` (e.g. `1-500,1000-2500`) and from the `range`
/// key of the configuration file.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PortRanges(pub Vec<(u16, u16)>);
/// Accepted spellings of `range` in the configuration file.
#[derive(Deserialize)]
#[serde(untagged)]
enum PortRangesConfig {
/// `range = { start = 1, end = 1000 }`: the format used before multiple
/// ranges were supported, kept so existing configuration files still work.
Single { start: u16, end: u16 },
/// `range = "1-500,1000-2500"`: the same syntax as `--range`.
Text(String),
/// `range = [[1, 500], [1000, 2500]]`.
Pairs(Vec<(u16, u16)>),
}
impl<'de> serde::Deserialize<'de> for PortRanges {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let pairs = match PortRangesConfig::deserialize(deserializer)? {
PortRangesConfig::Single { start, end } => vec![(start, end)],
PortRangesConfig::Text(text) => {
return parse_ranges(&text).map_err(serde::de::Error::custom)
}
PortRangesConfig::Pairs(pairs) => pairs,
};
if pairs.is_empty() {
return Err(serde::de::Error::custom("expected at least one port range"));
}
if let Some(&(start, end)) = pairs.iter().find(|&&(start, end)| start > end) {
return Err(serde::de::Error::custom(format!(
"invalid port range {start}-{end}: start must not be greater than end"
)));
}
Ok(PortRanges(pairs))
}
}
#[cfg(not(tarpaulin_include))]
fn parse_range(input: &str) -> Result<PortRange, String> {
let range = input
.split('-')
.map(str::parse)
.collect::<Result<Vec<u16>, std::num::ParseIntError>>();
/// Parse a single `start-end` token (e.g. "100-200") into `(start, end)`.
/// Returns `None` when the token is malformed, cannot be parsed as `u16`,
/// or `start > end`.
fn parse_range(input: &str) -> Option<(u16, u16)> {
let mut parts = input.trim().splitn(2, '-').map(str::trim);
let a = parts.next()?;
let b = parts.next()?;
if range.is_err() {
return Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
));
}
let start = a.parse::<u16>().ok()?;
let end = b.parse::<u16>().ok()?;
match range.unwrap().as_slice() {
[start, end] => Ok(PortRange {
start: *start,
end: *end,
}),
_ => Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
)),
if start <= end {
Some((start, end))
} else {
None
}
}
#[cfg(not(tarpaulin_include))]
/// Parse a comma-separated list of `start-end` ranges into `PortRanges`.
///
/// Errors with a helpful message identifying the bad token.
fn parse_ranges(input: &str) -> Result<PortRanges, String> {
let s = input.trim();
if s.is_empty() {
return Err("empty input: expected one or more comma-separated 'start-end' pairs".into());
}
#[derive(StructOpt, Debug, Clone)]
#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
let ranges_res: Result<Vec<(u16, u16)>, String> = s
.split(',')
.map(|token| {
let t = token.trim();
parse_range(t).ok_or_else(|| {
format!(
"invalid range token `{}` — expected `start-end` with 0 <= start <= end <= 65535",
t
)
})
})
.collect();
ranges_res.map(PortRanges)
}
#[derive(Parser, Debug, Clone)]
#[command(
name = "rustscan",
version = env!("CARGO_PKG_VERSION"),
max_term_width = 120,
help_template = "{bin} {version}\n{about}\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}",
)]
#[allow(clippy::struct_excessive_bools)]
/// 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.
#[structopt(short, long, use_delimiter = true)]
/// A comma-delimited list or newline-delimited file of separated CIDRs, IPs, or hosts to be scanned.
#[arg(short, long, value_delimiter = ',')]
pub addresses: Vec<String>,
/// A list of comma separed ports to be scanned. Example: 80,443,8080.
#[structopt(short, long, use_delimiter = true)]
/// A list of comma separated ports to be scanned. Example: 80,443,8080.
#[arg(short, long, value_delimiter = ',')]
pub ports: Option<Vec<u16>>,
/// A range of ports with format start-end. Example: 1-1000.
#[structopt(short, long, conflicts_with = "ports", parse(try_from_str = parse_range))]
pub range: Option<PortRange>,
/// Ranges of ports as comma-separated start-end pairs. Example: 1-500,1000-2500,4000-7000
#[arg(short, long, conflicts_with = "ports", value_parser = parse_ranges)]
pub range: Option<PortRanges>,
/// Whether to ignore the configuration file or not.
#[structopt(short, long)]
#[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>,
/// Greppable mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
#[structopt(short, long)]
#[arg(short, long)]
pub greppable: bool,
/// Accessible mode. Turns off features which negatively affect screen readers.
#[structopt(long)]
#[arg(long)]
pub accessible: bool,
/// A comma-delimited list or file of DNS resolvers.
#[arg(long)]
pub resolver: Option<String>,
/// The batch size for port scanning, it increases or slows the speed of
/// scanning. Depends on the open file limit of your OS. If you do 65535
/// it will do every port at the same time. Although, your OS may not
/// support this.
#[structopt(short, long, default_value = "4500")]
pub batch_size: u16,
#[arg(short, long, default_value = "4500")]
pub batch_size: usize,
/// The timeout in milliseconds before a port is assumed to be closed.
#[structopt(short, long, default_value = "1500")]
#[arg(short, long, default_value = "1500")]
pub timeout: u32,
/// The number of tries before a port is assumed to be closed.
/// If set to 0, rustscan will correct it to 1.
#[structopt(long, default_value = "1")]
#[arg(long, default_value = "1")]
pub tries: u8,
/// Automatically ups the ULIMIT with the value you provided.
#[structopt(short, long)]
pub ulimit: Option<rlimit::RawRlim>,
/// Automatically increases the Unix file-descriptor limit.
#[cfg_attr(not(unix), arg(hide = true))]
#[arg(short, long)]
pub ulimit: Option<usize>,
/// The order of scanning to be performed. The "serial" option will
/// scan ports in ascending order while the "random" option will scan
/// ports randomly.
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
#[arg(long, value_enum, ignore_case = true, default_value = "serial")]
pub scan_order: ScanOrder,
/// Level of scripting required for the run.
#[structopt(long, possible_values = &ScriptsRequired::variants(), case_insensitive = true, default_value = "default")]
#[arg(long, value_enum, ignore_case = true, default_value = "default")]
pub scripts: ScriptsRequired,
/// Use the top 1000 ports.
#[structopt(long)]
#[arg(long)]
pub top: bool,
/// 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)'\"")
#[structopt(last = true)]
/// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"
#[arg(last = true)]
pub command: Vec<String>,
/// A list of comma separated ports to be excluded from scanning. Example: 80,443,8080.
#[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,
/// Also list TCP ports that actively refused the connection (closed).
/// Closed ports are only printed; scripts are never run against them.
#[arg(long)]
pub closed: bool,
/// Milliseconds to wait after scanning a port (on every address) before
/// scanning the next one, for slow, low-noise scans. 0 disables the delay.
#[arg(long, default_value = "0", value_name = "MS")]
pub interval: u64,
/// Ids of the options given explicitly on the command line. The
/// configuration file never overrides these (see [`Opts::merge`]).
#[arg(skip)]
pub cli_args: HashSet<String>,
}
#[cfg(not(tarpaulin_include))]
impl Opts {
pub fn read() -> Self {
let mut opts = Opts::from_args();
Self::try_read_from(std::env::args_os()).unwrap_or_else(|e| e.exit())
}
/// Parses `args` like [`Opts::read`] does, returning an error instead of
/// exiting.
fn try_read_from<I, T>(args: I) -> Result<Self, clap::Error>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let mut command = Self::command();
let mut matches = command.try_get_matches_from_mut(args)?;
// clap fills in default values, so the parsed struct alone cannot
// tell `-b 4500` apart from no `-b` at all. Record what the user
// actually typed so that `merge` lets it win over the config file.
let cli_args = command
.get_arguments()
.map(|arg| arg.get_id().as_str())
.filter(|id| matches.value_source(id) == Some(ValueSource::CommandLine))
.map(str::to_owned)
.collect();
let mut opts =
Self::from_arg_matches_mut(&mut matches).map_err(|e| e.format(&mut command))?;
opts.cli_args = cli_args;
if opts.ports.is_none() && opts.range.is_none() {
opts.range = Some(PortRange {
start: LOWEST_PORT_NUMBER,
end: TOP_PORT_NUMBER,
});
opts.range = Some(PortRanges(vec![(LOWEST_PORT_NUMBER, TOP_PORT_NUMBER)]));
}
opts
Ok(opts)
}
/// Validates options whose availability or semantics depend on the
/// operating system.
///
/// # Errors
///
/// Returns an error when an option is unsupported on the current platform.
pub fn validate_platform(&self) -> Result<(), String> {
#[cfg(not(unix))]
{
if self.ulimit.is_some() {
return Err(
"--ulimit is only supported on Unix-like operating systems. \
On Windows, use --batch-size (-b) to control scan concurrency."
.to_owned(),
);
}
}
Ok(())
}
/// Reads the command line arguments into an Opts struct and merge
/// values found within the user configuration file.
/// values found within the user configuration file. Options given on the
/// command line take precedence over the 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);
}
}
@@ -166,7 +314,9 @@ impl Opts {
($($field: ident),+) => {
$(
if let Some(e) = &config.$field {
self.$field = e.clone();
if !self.cli_args.contains(stringify!($field)) {
self.$field = e.clone();
}
}
)+
}
@@ -174,7 +324,7 @@ impl Opts {
merge_required!(
addresses, greppable, accessible, batch_size, timeout, tries, scan_order, scripts,
command
command, udp, no_banner, closed, interval
);
}
@@ -182,7 +332,7 @@ impl Opts {
macro_rules! merge_optional {
($($field: ident),+) => {
$(
if config.$field.is_some() {
if config.$field.is_some() && !self.cli_args.contains(stringify!($field)) {
self.$field = config.$field.clone();
}
)+
@@ -191,14 +341,40 @@ impl Opts {
// Only use top ports when the user asks for them
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())
}
self.ports = Some(ports);
self.ports = config.ports.clone();
}
merge_optional!(range, ulimit);
merge_optional!(range, resolver, ulimit, exclude_ports, exclude_addresses);
}
}
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,
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,
closed: false,
interval: 0,
cli_args: HashSet::new(),
}
}
}
@@ -209,20 +385,29 @@ impl Opts {
#[derive(Debug, Deserialize)]
pub struct Config {
addresses: Option<Vec<String>>,
ports: Option<HashMap<String, u16>>,
range: Option<PortRange>,
ports: Option<Vec<u16>>,
range: Option<PortRanges>,
greppable: Option<bool>,
accessible: Option<bool>,
batch_size: Option<u16>,
batch_size: Option<usize>,
timeout: Option<u32>,
tries: Option<u8>,
ulimit: Option<rlimit::RawRlim>,
ulimit: Option<usize>,
resolver: Option<String>,
scan_order: Option<ScanOrder>,
command: Option<Vec<String>>,
scripts: Option<ScriptsRequired>,
exclude_ports: Option<Vec<u16>>,
exclude_addresses: Option<Vec<String>>,
udp: Option<bool>,
no_banner: Option<bool>,
closed: Option<bool>,
interval: Option<u64>,
}
#[cfg(not(tarpaulin_include))]
#[allow(clippy::doc_link_with_quotes)]
#[allow(clippy::manual_unwrap_or_default)]
impl Config {
/// Reads the configuration file with TOML format and parses it into a
/// Config struct.
@@ -232,18 +417,31 @@ 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
/// closed = false
/// interval = 0
///
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(|| {
// Try the XDG-idiomatic location first, then fall back to legacy
// paths so existing users keep working unchanged.
for path in [
default_config_path(),
legacy_dot_config_path(),
old_default_config_path(),
] {
if path.exists() {
return path;
}
}
default_config_path()
});
if config_path.exists() {
content = match fs::read_to_string(config_path) {
Ok(content) => content,
Err(_) => String::new(),
}
@@ -252,7 +450,9 @@ impl Config {
let config: Config = match toml::from_str(&content) {
Ok(config) => config,
Err(e) => {
println!("Found {} in configuration file.\nAborting scan.\n", e);
crate::tui::println_safe(format_args!(
"Found {e} in configuration file.\nAborting scan.\n"
));
std::process::exit(1);
}
};
@@ -261,9 +461,45 @@ impl Config {
}
}
/// Returns the preferred config file path: `$XDG_CONFIG_HOME/rustscan/config.toml`
/// on Linux (with the usual `~/.config` fallback when the variable is unset),
/// and the platform-equivalent `dirs::config_dir()` location on macOS / Windows.
pub fn default_config_path() -> PathBuf {
let Some(mut config_path) = dirs::config_dir() else {
panic!("Could not infer config file path.");
};
config_path.push("rustscan");
config_path.push("config.toml");
config_path
}
/// Returns the transitional `$XDG_CONFIG_HOME/.rustscan.toml` path that older
/// builds wrote to. Kept readable for backwards compatibility.
pub fn legacy_dot_config_path() -> PathBuf {
let Some(mut config_path) = dirs::config_dir() else {
panic!("Could not infer config file path.");
};
config_path.push(".rustscan.toml");
config_path
}
/// Returns the deprecated home directory config path used for backwards compatibility.
pub fn old_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};
use clap::{CommandFactory, Parser};
use parameterized::parameterized;
use std::collections::HashSet;
use super::{Config, Opts, PortRanges, ScanOrder, ScriptsRequired};
impl Config {
fn default() -> Self {
Self {
@@ -277,31 +513,118 @@ mod tests {
ulimit: None,
command: Some(vec!["-A".to_owned()]),
accessible: Some(true),
resolver: None,
scan_order: Some(ScanOrder::Random),
scripts: None,
exclude_ports: None,
exclude_addresses: None,
udp: Some(false),
no_banner: None,
closed: Some(false),
interval: 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,
}
}
#[test]
fn verify_cli() {
Opts::command().debug_assert();
}
#[parameterized(input = {
vec!["rustscan", "--addresses", "127.0.0.1"],
vec!["rustscan", "--addresses", "127.0.0.1", "--", "-sCV"],
vec!["rustscan", "--addresses", "127.0.0.1", "--", "-A"],
vec!["rustscan", "-t", "1500", "-a", "127.0.0.1", "--", "-A", "-sC"],
vec!["rustscan", "--addresses", "127.0.0.1", "--", "--script", r#""'(safe and vuln)'""#],
}, command = {
vec![],
vec!["-sCV".to_owned()],
vec!["-A".to_owned()],
vec!["-A".to_owned(), "-sC".to_owned()],
vec!["--script".to_owned(), "\"'(safe and vuln)'\"".to_owned()],
})]
fn parse_trailing_command(input: Vec<&str>, command: Vec<String>) {
let opts = Opts::parse_from(input);
assert_eq!(vec!["127.0.0.1".to_owned()], opts.addresses);
assert_eq!(command, opts.command);
}
#[test]
fn parses_explicit_batch_size() {
let opts = Opts::parse_from(["rustscan", "-a", "127.0.0.1", "-b", "1234"]);
assert_eq!(opts.batch_size, 1234);
}
#[test]
fn parses_explicit_long_batch_size() {
let opts = Opts::parse_from(["rustscan", "-a", "127.0.0.1", "--batch-size", "4321"]);
assert_eq!(opts.batch_size, 4321);
}
#[test]
#[cfg(windows)]
fn windows_platform_validation_accepts_batch_size() {
let opts = Opts::parse_from(["rustscan", "-a", "127.0.0.1", "--batch-size", "500"]);
assert!(opts.validate_platform().is_ok());
assert_eq!(opts.batch_size, 500);
}
#[test]
#[cfg(windows)]
fn windows_rejects_ulimit() {
let opts = Opts::parse_from(["rustscan", "-a", "127.0.0.1", "--ulimit", "5000"]);
let error = opts
.validate_platform()
.expect_err("Windows must reject --ulimit");
assert!(error.contains("--ulimit"));
assert!(error.contains("Unix"));
assert!(error.contains("--batch-size"));
}
#[test]
#[cfg(windows)]
fn windows_hides_ulimit_from_help() {
let help = Opts::command().render_long_help().to_string();
assert!(
!help.contains("--ulimit"),
"--ulimit should not be advertised on Windows"
);
}
#[test]
#[cfg(windows)]
fn windows_rejects_ulimit_from_config_merge() {
let mut opts = Opts {
no_config: false,
..Default::default()
};
let mut config = Config::default();
config.ulimit = Some(5_000);
opts.merge(&config);
let error = opts
.validate_platform()
.expect_err("Windows must reject --ulimit supplied by configuration");
assert!(error.contains("--ulimit"));
}
#[test]
#[cfg(unix)]
fn unix_platform_validation_accepts_ulimit() {
let opts = Opts::parse_from(["rustscan", "-a", "127.0.0.1", "--ulimit", "5000"]);
assert!(opts.validate_platform().is_ok());
assert_eq!(opts.ulimit, Some(5_000));
}
#[test]
@@ -312,8 +635,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,22 +655,176 @@ 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]
fn opts_merge_optional_arguments() {
let mut opts = Opts::default();
let mut config = Config::default();
config.range = Some(PortRange {
start: 1,
end: 1_000,
});
config.range = Some(PortRanges(vec![(1, 1_000)]));
config.ulimit = Some(1_000);
config.resolver = Some("1.1.1.1".to_owned());
opts.merge_optional(&config);
assert_eq!(opts.range, config.range);
assert_eq!(opts.ulimit, config.ulimit);
assert_eq!(opts.resolver, config.resolver);
}
#[test]
fn cli_arguments_override_config() {
// Regression test for #722: the config file used to silently replace
// options given on the command line, e.g. `-r` with its `range`.
let mut opts = Opts::try_read_from([
"rustscan",
"-a",
"192.168.0.1",
"-r",
"8600-8650",
"-b",
"100",
// Passing clap's default value explicitly must still win.
"-t",
"1500",
"--tries",
"3",
"--scan-order",
"serial",
"--scripts",
"none",
"-g",
"--",
"-sV",
])
.unwrap();
let config: Config = toml::from_str(
r#"
addresses = ["127.0.0.1"]
range = { start = 1, end = 100 }
batch_size = 25000
timeout = 1000
tries = 1
scan_order = "Random"
scripts = "Custom"
greppable = false
command = ["-A"]
"#,
)
.unwrap();
opts.merge(&config);
assert_eq!(opts.addresses, vec!["192.168.0.1".to_owned()]);
assert_eq!(opts.range, Some(PortRanges(vec![(8_600, 8_650)])));
assert_eq!(opts.batch_size, 100);
assert_eq!(opts.timeout, 1_500);
assert_eq!(opts.tries, 3);
assert_eq!(opts.scan_order, ScanOrder::Serial);
assert_eq!(opts.scripts, ScriptsRequired::None);
assert!(opts.greppable);
assert_eq!(opts.command, vec!["-sV".to_owned()]);
}
#[test]
fn config_fills_arguments_not_given_on_cli() {
let mut opts = Opts::try_read_from(["rustscan", "-a", "192.168.0.1"]).unwrap();
let config: Config = toml::from_str(
r#"
addresses = ["127.0.0.1"]
range = { start = 1, end = 100 }
batch_size = 25000
timeout = 1000
tries = 3
scan_order = "Random"
greppable = true
command = ["-A"]
"#,
)
.unwrap();
opts.merge(&config);
// Only `-a` was typed; clap defaults such as `-b 4500` must not count.
assert_eq!(opts.cli_args, HashSet::from(["addresses".to_owned()]));
assert_eq!(opts.addresses, vec!["192.168.0.1".to_owned()]);
assert_eq!(opts.range, Some(PortRanges(vec![(1, 100)])));
assert_eq!(opts.batch_size, 25_000);
assert_eq!(opts.timeout, 1_000);
assert_eq!(opts.tries, 3);
assert_eq!(opts.scan_order, ScanOrder::Random);
assert!(opts.greppable);
assert_eq!(opts.command, vec!["-A".to_owned()]);
}
#[test]
fn parses_comma_separated_ranges() {
let opts = Opts::parse_from([
"rustscan",
"-a",
"127.0.0.1",
"-r",
"1-100, 200-300,5000-5100",
]);
assert_eq!(
opts.range,
Some(PortRanges(vec![(1, 100), (200, 300), (5_000, 5_100)]))
);
}
#[test]
fn rejects_malformed_ranges() {
for range in ["", "300-200", "1-100,", "1-2-3", "a-b", "1-70000"] {
assert!(
Opts::try_parse_from(["rustscan", "-a", "127.0.0.1", "-r", range]).is_err(),
"{:?} should be rejected",
range
);
}
}
#[test]
fn config_range_accepts_legacy_table() {
let config: Config = toml::from_str("range = { start = 1, end = 1000 }").unwrap();
assert_eq!(config.range, Some(PortRanges(vec![(1, 1_000)])));
}
#[test]
fn config_range_accepts_string_and_pairs() {
let expected = Some(PortRanges(vec![(1, 100), (200, 300)]));
let config: Config = toml::from_str("range = \"1-100,200-300\"").unwrap();
assert_eq!(config.range, expected);
let config: Config = toml::from_str("range = [[1, 100], [200, 300]]").unwrap();
assert_eq!(config.range, expected);
}
#[test]
fn config_range_rejects_invalid_ranges() {
for range in [
"range = { start = 10, end = 1 }",
"range = [[10, 1]]",
"range = []",
"range = \"10-1\"",
] {
assert!(
toml::from_str::<Config>(range).is_err(),
"{:?} should be rejected",
range
);
}
}
#[test]
fn parses_interval_in_milliseconds() {
let opts = Opts::parse_from(["rustscan", "-a", "127.0.0.1", "--interval", "250"]);
assert_eq!(opts.interval, 250);
let opts = Opts::parse_from(["rustscan", "-a", "127.0.0.1"]);
assert_eq!(opts.interval, 0);
}
}
+57
View File
@@ -0,0 +1,57 @@
//! 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):
//!
//! This example is compiled but never executed by the test suite.
//!
//! ```no_run
//! use async_std::task::block_on;
//! use std::{net::IpAddr, time::Duration};
//!
//! use rustscan::input::{PortRanges, ScanOrder};
//! use rustscan::port_strategy::PortStrategy;
//! use rustscan::scanner::Scanner;
//!
//! fn main() {
//! let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
//! let range = PortRanges(vec![(1, 1_000)]);
//! 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, // 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());
//!
//! 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;
pub mod address;
pub mod generated;
+193 -259
View File
@@ -2,49 +2,32 @@
#![warn(clippy::pedantic)]
#![allow(clippy::doc_markdown, clippy::if_not_else, clippy::non_ascii_literal)]
extern crate shell_words;
use rustscan::benchmark::{Benchmark, NamedTimer};
use rustscan::input::{self, Config, Opts, ScriptsRequired};
use rustscan::port_strategy::PortStrategy;
use rustscan::scanner::{PortStatus, Scanner};
use rustscan::scripts::{init_scripts, Script, ScriptFile};
use rustscan::tui::println_safe;
use rustscan::{detail, funny_opening, output, warning};
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 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};
use std::path::Path;
use std::net::IpAddr;
use std::string::ToString;
use std::time::Duration;
use trust_dns_resolver::{
config::{ResolverConfig, ResolverOpts},
Resolver,
};
use rustscan::address::parse_addresses;
extern crate colorful;
extern crate dirs;
// Average value for Ubuntu
const DEFAULT_FILE_DESCRIPTORS_LIMIT: RawRlim = 8000;
#[cfg(unix)]
const DEFAULT_FILE_DESCRIPTORS_LIMIT: usize = 8000;
// Safest batch size based on experimentation
const AVERAGE_BATCH_SIZE: RawRlim = 3000;
#[cfg(unix)]
const AVERAGE_BATCH_SIZE: usize = 3000;
#[macro_use]
extern crate log;
@@ -54,21 +37,29 @@ extern crate log;
/// Faster Nmap scanning with Rust
/// If you're looking for the actual scanning, check out the module Scanner
fn main() {
#[cfg(not(unix))]
let _ = ansi_term::enable_ansi_support();
env_logger::init();
let mut benchmarks = Benchmark::init();
let mut rustscan_bench = NamedTimer::start("RustScan");
let mut opts: Opts = Opts::read();
let config = Config::read();
let config = Config::read(opts.config_path.clone());
opts.merge(&config);
debug!("Main() `opts` arguments are {:?}", opts);
if let Err(message) = opts.validate_platform() {
eprintln!("error: {message}");
std::process::exit(2);
}
let scripts_to_run: Vec<ScriptFile> = match init_scripts(opts.scripts) {
debug!("Main() `opts` arguments are {opts:?}");
let scripts_to_run: Vec<ScriptFile> = match init_scripts(&opts.scripts) {
Ok(scripts_to_run) => scripts_to_run,
Err(e) => {
warning!(
format!("Initiating scripts failed!\n{}", e),
format!("Initiating scripts failed!\n{e}"),
opts.greppable,
opts.accessible
);
@@ -76,9 +67,9 @@ fn main() {
}
};
debug!("Scripts initialized {:?}", &scripts_to_run);
debug!("Scripts initialized {scripts_to_run:?}");
if !opts.greppable && !opts.accessible {
if !opts.greppable && !opts.accessible && !opts.no_banner {
print_opening(&opts);
}
@@ -93,10 +84,10 @@ fn main() {
std::process::exit(1);
}
let ulimit: RawRlim = adjust_ulimit_size(&opts);
let batch_size: u16 = infer_batch_size(&opts, ulimit);
let batch_size = effective_batch_size(&opts);
debug!("Effective batch size: {batch_size}");
let scanner = Scanner::new(
let mut scanner = Scanner::new(
&ips,
batch_size,
Duration::from_millis(opts.timeout.into()),
@@ -104,21 +95,32 @@ fn main() {
opts.greppable,
PortStrategy::pick(&opts.range, opts.ports, opts.scan_order),
opts.accessible,
);
debug!("Scanner finished building: {:?}", scanner);
opts.exclude_ports.unwrap_or_default(),
opts.udp,
)
.with_open_port_output();
if opts.closed {
scanner = scanner.with_closed_ports();
}
if opts.interval > 0 {
scanner = scanner.with_interval(Duration::from_millis(opts.interval));
}
debug!("Scanner finished building: {scanner:?}");
let mut portscan_bench = NamedTimer::start("Portscan");
let scan_result = block_on(scanner.run());
let scan_result = block_on(scanner.run_with_status());
portscan_bench.end();
benchmarks.push(portscan_bench);
let mut ports_per_ip = HashMap::new();
let mut ports_per_ip: HashMap<IpAddr, Vec<u16>> = HashMap::new();
let mut closed_ports_per_ip: HashMap<IpAddr, Vec<u16>> = HashMap::new();
for socket in scan_result {
ports_per_ip
.entry(socket.ip())
.or_insert_with(Vec::new)
.push(socket.port());
for status in scan_result {
let (map, socket) = match status {
PortStatus::Open(socket) => (&mut ports_per_ip, socket),
PortStatus::Closed(socket) => (&mut closed_ports_per_ip, socket),
};
map.entry(socket.ip()).or_default().push(socket.port());
}
for ip in ips {
@@ -133,7 +135,7 @@ fn main() {
\n*I used {} batch size, consider lowering it with {} or a comfortable number for your system.
\n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n",
ip,
opts.batch_size,
batch_size,
"'rustscan -b <batch_size> -a <ip address>'");
warning!(x, opts.greppable, opts.accessible);
}
@@ -147,17 +149,19 @@ fn main() {
// if option scripts is none, no script will be spawned
if opts.greppable || opts.scripts == ScriptsRequired::None {
println!("{} -> [{}]", &ip, ports_str);
println_safe(format_args!("{ip} -> [{ports_str}]"));
continue;
}
detail!("Starting Script(s)", opts.greppable, opts.accessible);
// Run all the scripts we found and parsed based on the script config file tags field.
for mut script_f in scripts_to_run.clone() {
for script_f in &scripts_to_run {
// Clone only the script being run instead of the whole list per IP.
let mut script_f = script_f.clone();
// This part allows us to add commandline arguments to the Script call_format, appending them to the end of the command.
if !opts.command.is_empty() {
let user_extra_args = &opts.command.join(" ");
debug!("Extra args vec {:?}", user_extra_args);
debug!("Extra args vec {user_extra_args:?}");
if script_f.call_format.is_some() {
let mut call_f = script_f.call_format.unwrap();
call_f.push(' ');
@@ -167,7 +171,7 @@ fn main() {
opts.greppable,
opts.accessible
);
debug!("Call format {}", call_f);
debug!("Call format {call_f}");
script_f.call_format = Some(call_f);
}
}
@@ -184,25 +188,56 @@ fn main() {
);
match script.run() {
Ok(script_result) => {
detail!(script_result.to_string(), opts.greppable, opts.accessible);
detail!(script_result.clone(), opts.greppable, opts.accessible);
}
Err(e) => {
warning!(&format!("Error {}", e), opts.greppable, opts.accessible);
warning!(&format!("Error {e}"), opts.greppable, opts.accessible);
}
}
}
}
// Closed ports are only listed; scripts are never run against them.
if opts.closed {
println_safe(format_args!("closed ports:"));
for (ip, ports) in &closed_ports_per_ip {
let ports_str = ports
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(",");
println_safe(format_args!("{ip} -> [{ports_str}]"));
}
}
// To use the runtime benchmark, run the process as: RUST_LOG=info ./rustscan
script_bench.end();
benchmarks.push(script_bench);
rustscan_bench.end();
benchmarks.push(rustscan_bench);
debug!("Benchmarks raw {:?}", benchmarks);
debug!("Benchmarks raw {benchmarks:?}");
info!("{}", benchmarks.summary());
}
/// Determines the actual batch size used by the scanner.
///
/// Unix systems may reduce the requested batch size according to the process
/// file-descriptor limit. Other platforms, including Windows, use the batch
/// size explicitly requested by the user.
fn effective_batch_size(opts: &Opts) -> usize {
#[cfg(unix)]
{
infer_batch_size(opts, adjust_ulimit_size(opts))
}
#[cfg(not(unix))]
{
opts.batch_size
}
}
/// 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#".----. .-. .-. .----..---. .----. .---. .--. .-. .-.
@@ -210,130 +245,48 @@ fn print_opening(opts: &Opts) {
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner."#;
println!("{}", s.gradient(Color::Green).bold());
println_safe(format_args!("{}", s.gradient(Color::Green).bold()));
let info = r#"________________________________________
: https://discord.gg/GFrQsGy :
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------"#;
println!("{}", info.gradient(Color::Yellow).bold());
println_safe(format_args!("{}", 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
);
}
/// Goes through all possible IP inputs (files or via argparsing)
/// Parses the string(s) into IPs
fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
let mut ips: Vec<IpAddr> = Vec::new();
let mut unresolved_addresses: Vec<&str> = Vec::new();
let backup_resolver =
Resolver::new(ResolverConfig::cloudflare_tls(), ResolverOpts::default()).unwrap();
for address in &input.addresses {
let parsed_ips = parse_address(address, &backup_resolver);
if !parsed_ips.is_empty() {
ips.extend(parsed_ips);
} else {
unresolved_addresses.push(address);
}
if opts.config_path.is_none() {
let old_config_path = input::old_default_config_path();
detail!(
format!(
"For backwards compatibility, the config file may also be at {old_config_path:?}"
),
opts.greppable,
opts.accessible
);
}
// If we got to this point this can only be a file path or the wrong input.
for file_path in unresolved_addresses {
let file_path = Path::new(file_path);
if !file_path.is_file() {
warning!(
format!("Host {:?} could not be resolved.", file_path),
input.greppable,
input.accessible
);
continue;
}
if let Ok(x) = read_ips_from_file(file_path, &backup_resolver) {
ips.extend(x);
} else {
warning!(
format!("Host {:?} could not be resolved.", file_path),
input.greppable,
input.accessible
);
}
}
ips
}
/// Given a string, parse it as an host, IP address, or CIDR.
/// This allows us to pass files as hosts or cidr or IPs easily
/// Call this everytime you have a possible IP_or_host
fn parse_address(address: &str, resolver: &Resolver) -> Vec<IpAddr> {
IpCidr::from_str(&address)
.map(|cidr| cidr.iter().collect())
.ok()
.or_else(|| {
format!("{}:{}", &address, 80)
.to_socket_addrs()
.ok()
.map(|mut iter| vec![iter.next().unwrap().ip()])
})
.unwrap_or_else(|| resolve_ips_from_host(address, resolver))
}
#[cfg(unix)]
fn adjust_ulimit_size(opts: &Opts) -> usize {
use rlimit::Resource;
use std::convert::TryInto;
/// Uses DNS to get the IPS assiocated with host
fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec<IpAddr> {
let mut ips: Vec<std::net::IpAddr> = Vec::new();
if let Ok(addrs) = source.to_socket_addrs() {
for ip in addrs {
ips.push(ip.ip());
}
} else if let Ok(addrs) = backup_resolver.lookup_ip(source) {
ips.extend(addrs.iter());
}
ips
}
#[cfg(not(tarpaulin_include))]
/// Parses an input file of IPs and uses those
fn read_ips_from_file(
ips: &std::path::Path,
backup_resolver: &Resolver,
) -> Result<Vec<std::net::IpAddr>, std::io::Error> {
let file = File::open(ips)?;
let reader = BufReader::new(file);
let mut ips: Vec<std::net::IpAddr> = Vec::new();
for address_line in reader.lines() {
if let Ok(address) = address_line {
ips.extend(parse_address(&address, backup_resolver));
} else {
debug!("Line in file is not valid");
}
}
Ok(ips)
}
fn adjust_ulimit_size(opts: &Opts) -> RawRlim {
if opts.ulimit.is_some() {
let limit: Rlim = Rlim::from_raw(opts.ulimit.unwrap());
if setrlimit(Resource::NOFILE, limit, limit).is_ok() {
if let Some(limit) = opts.ulimit {
let limit = limit as u64;
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
);
@@ -346,13 +299,16 @@ fn adjust_ulimit_size(opts: &Opts) -> RawRlim {
}
}
let (rlim, _) = getrlimit(Resource::NOFILE).unwrap();
rlim.as_raw()
let (soft, _) = Resource::NOFILE.get().unwrap();
// On 32-bit targets the soft limit (e.g. RLIM_INFINITY) may not fit in a
// usize. Fall back to the conservative default rather than usize::MAX,
// which would skip every batch-size adjustment in infer_batch_size.
soft.try_into().unwrap_or(DEFAULT_FILE_DESCRIPTORS_LIMIT)
}
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: usize) -> usize {
let mut batch_size = opts.batch_size;
// Adjust the batch size when the ulimit value is lower than the desired batch size
if ulimit < batch_size {
@@ -364,7 +320,7 @@ fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 {
// selected a batch size higher than this we should reduce it to
// a lower number.
if ulimit < AVERAGE_BATCH_SIZE {
// ulimit is smaller than aveage batch size
// ulimit is smaller than average 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);
@@ -385,139 +341,117 @@ fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 {
}
batch_size
.try_into()
.expect("Couldn't fit the batch size into a u16.")
}
#[cfg(test)]
mod tests {
use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts};
use std::net::Ipv4Addr;
#[cfg(windows)]
use super::effective_batch_size;
#[cfg(unix)]
use super::{adjust_ulimit_size, infer_batch_size};
use super::{print_opening, Opts};
#[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);
}
#[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);
assert_eq!(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
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);
assert_eq!(batch_size, 4_900);
}
#[test]
#[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);
assert_eq!(batch_size, 2_000);
}
#[test]
#[cfg(unix)]
fn test_high_ulimit_no_greppable_mode() {
let opts = Opts {
batch_size: 10,
greppable: false,
..Default::default()
};
let batch_size = infer_batch_size(&opts, 1_000_000);
assert_eq!(batch_size, opts.batch_size);
}
#[test]
fn test_print_opening_no_panic() {
let mut opts = Opts::default();
opts.ulimit = Some(2_000);
let opts = Opts::default();
// print opening should not panic
print_opening(&opts);
}
#[test]
fn test_high_ulimit_no_greppable_mode() {
let mut opts = Opts::default();
opts.batch_size = 10;
opts.greppable = false;
#[cfg(windows)]
fn windows_batch_size_uses_requested_value() {
let opts = Opts {
batch_size: 50,
..Default::default()
};
let batch_size = infer_batch_size(&opts, 1_000_000);
assert!(batch_size == opts.batch_size);
assert_eq!(effective_batch_size(&opts), 50);
}
#[test]
fn parse_correct_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()];
let ips = parse_addresses(&opts);
#[cfg(windows)]
fn windows_batch_size_preserves_large_requested_value() {
let opts = Opts {
batch_size: 12_345,
..Default::default()
};
assert_eq!(
ips,
[
Ipv4Addr::new(127, 0, 0, 1),
Ipv4Addr::new(192, 168, 0, 0),
Ipv4Addr::new(192, 168, 0, 1),
Ipv4Addr::new(192, 168, 0, 2),
Ipv4Addr::new(192, 168, 0, 3)
]
);
assert_eq!(effective_batch_size(&opts), 12_345);
}
#[test]
fn parse_correct_host_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["google.com".to_owned()];
let ips = parse_addresses(&opts);
#[cfg(windows)]
fn windows_batch_size_preserves_single_connection() {
let opts = Opts {
batch_size: 1,
..Default::default()
};
assert_eq!(ips.len(), 1);
}
#[test]
fn parse_correct_and_incorrect_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()];
let ips = parse_addresses(&opts);
assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]);
}
#[test]
fn parse_incorrect_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()];
let ips = parse_addresses(&opts);
assert_eq!(ips.is_empty(), true);
}
#[test]
fn parse_hosts_file_and_incorrect_hosts() {
// Host file contains IP, Hosts, incorrect IPs, incorrect hosts
let mut opts = Opts::default();
opts.addresses = vec!["fixtures/hosts.txt".to_owned()];
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 3);
}
#[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 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 ips = parse_addresses(&opts);
assert_eq!(ips.len(), 0);
assert_eq!(effective_batch_size(&opts), 1);
}
}
+43 -32
View File
@@ -1,7 +1,8 @@
//! Provides a means to hold configuration options specifically for port scanning.
mod range_iterator;
use super::{PortRange, ScanOrder};
use crate::input::{PortRanges, ScanOrder};
use rand::rng;
use rand::seq::SliceRandom;
use rand::thread_rng;
use range_iterator::RangeIterator;
/// Represents options of port scanning.
@@ -16,25 +17,23 @@ pub enum PortStrategy {
}
impl PortStrategy {
pub fn pick(range: &Option<PortRange>, ports: Option<Vec<u16>>, order: ScanOrder) -> Self {
pub fn pick(range: &Option<PortRanges>, ports: Option<Vec<u16>>, order: ScanOrder) -> Self {
match order {
ScanOrder::Serial if ports.is_none() => {
let range = range.as_ref().unwrap();
let port_ranges = range.as_ref().unwrap();
PortStrategy::Serial(SerialRange {
start: range.start,
end: range.end,
range: port_ranges.0.clone(),
})
}
ScanOrder::Random if ports.is_none() => {
let range = range.as_ref().unwrap();
let port_ranges = range.as_ref().unwrap();
PortStrategy::Random(RandomRange {
start: range.start,
end: range.end,
range: port_ranges.0.clone(),
})
}
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)
@@ -44,7 +43,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(),
}
@@ -61,13 +60,12 @@ trait RangeOrder {
/// ascending order.
#[derive(Debug)]
pub struct SerialRange {
start: u16,
end: u16,
range: Vec<(u16, u16)>,
}
impl RangeOrder for SerialRange {
fn generate(&self) -> Vec<u16> {
(self.start..self.end).collect()
RangeIterator::new_serial(&self.range).collect()
}
}
@@ -75,8 +73,7 @@ impl RangeOrder for SerialRange {
/// a random order. This vector is built following the LCG algorithm.
#[derive(Debug)]
pub struct RandomRange {
start: u16,
end: u16,
range: Vec<(u16, u16)>,
}
impl RangeOrder for RandomRange {
@@ -90,33 +87,47 @@ impl RangeOrder for RandomRange {
// port numbers close to each other are pretty slim due to the way the
// algorithm works.
fn generate(&self) -> Vec<u16> {
RangeIterator::new(self.start.into(), self.end.into()).collect()
RangeIterator::new_random(&self.range).collect()
}
}
#[cfg(test)]
mod tests {
use super::PortStrategy;
use crate::{PortRange, ScanOrder};
use crate::input::{PortRanges, ScanOrder};
use std::collections::HashSet;
fn expected_ports_from_ranges(input: &[(u16, u16)]) -> Vec<u16> {
let mut s = HashSet::new();
for &(start, end) in input {
for p in start..=end {
s.insert(p);
}
}
let mut v: Vec<u16> = s.into_iter().collect();
v.sort_unstable();
v
}
#[test]
fn serial_strategy_with_range() {
let range = PortRange { start: 1, end: 100 };
let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Serial);
let ranges = PortRanges(vec![(1u16, 10u16), (20u16, 30u16), (100u16, 110u16)]);
let strategy = PortStrategy::pick(&Some(ranges.clone()), None, ScanOrder::Serial);
let result = strategy.order();
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let expected = expected_ports_from_ranges(&ranges.0);
assert_eq!(expected, result);
}
#[test]
fn random_strategy_with_range() {
let range = PortRange { start: 1, end: 100 };
let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
let ranges = PortRanges(vec![(1u16, 10u16), (20u16, 30u16), (100u16, 110u16)]);
let strategy = PortStrategy::pick(&Some(ranges.clone()), None, ScanOrder::Random);
let mut result = strategy.order();
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_ne!(expected_range, result);
let expected = expected_ports_from_ranges(&ranges.0);
result.sort();
assert_eq!(expected_range, result);
assert_ne!(expected, result);
result.sort_unstable();
assert_eq!(expected, result);
}
#[test]
@@ -130,10 +141,10 @@ mod tests {
fn random_strategy_with_ports() {
let strategy = PortStrategy::pick(&None, Some((1..10).collect()), ScanOrder::Random);
let mut result = strategy.order();
let expected_range = (1..10).into_iter().collect::<Vec<u16>>();
assert_ne!(expected_range, result);
let expected = (1..10).collect::<Vec<u16>>();
assert_ne!(expected, result);
result.sort();
assert_eq!(expected_range, result);
result.sort_unstable();
assert_eq!(expected, result);
}
}
+255 -62
View File
@@ -1,71 +1,196 @@
use gcd::Gcd;
use rand::Rng;
use rand::RngExt;
use std::convert::TryInto;
/// Yields ports produced from a collection of (possibly overlapping)
/// inclusive `u16` ranges in two modes:
///
/// **Randomized** — created by `RangeIterator::new_random`:
/// - The algorithm generates a permutation of indices `0..N-1` using the
/// additive congruential step `x_{i+1} = (x_i + step) % N`.
/// - `step` is chosen so `gcd(step, N) == 1` to ensure the sequence is a
/// full-length cycle (visits each index exactly once).
/// - `x_0` (the seed stored as `normalized_first_pick`) is chosen uniformly
/// in `0..N`.
///
/// For more information: <https://en.wikipedia.org/wiki/Linear_congruential_generator>
///
/// **Serial** — `RangeIterator::new_serial`:
/// Iterates the input ranges in the **original input order** and yields
/// each port the first time it is encountered. Duplicate ports (from
/// overlapping ranges) are skipped using a 65_536-entry "seen" table.
pub struct RangeIterator {
active: bool,
normalized_end: u32,
total: u32,
normalized_first_pick: u32,
normalized_pick: u32,
actual_start: u32,
step: u32,
ranges: Vec<(u32, u32)>,
prefix: Vec<u32>,
serial_itr: Option<Box<dyn Iterator<Item = u16>>>,
/// `seen[port]` is true once `port` has been yielded (serial mode only).
serial_seen: Option<Vec<bool>>,
}
/// An iterator that follows the `Linear Congruential Generator` algorithm.
///
/// For more information: https://en.wikipedia.org/wiki/Linear_congruential_generator
impl RangeIterator {
/// Receives the the start and end of a range and normalize
/// these values before selecting a coprime for the end of the range
/// which will server as the step for the algorithm.
/// Construct a randomized iterator (LCG permutation).
///
/// For example, the range `1000-2500` will be normalized to `0-1500`
/// before going through the algorithm.
pub fn new(start: u32, end: u32) -> Self {
let normalized_end = end - start;
let step = pick_random_coprime(normalized_end);
/// Preconditions:
/// - `input` must contain at least one `(u16,u16)`
/// and each pair must satisfy `start <= end`.
pub fn new_random(input: &[(u16, u16)]) -> Self {
// normalize & merge into (start, len) u32 pairs
// Example: [(10,12),(11,15)] -> merged [(10,6)]
let mut ranges: Vec<(u32, u32)> = input
.iter()
.map(|(s, e)| {
let start = *s as u32;
let end_excl = (*e as u32) + 1; // convert inclusive -> exclusive
(start, end_excl)
})
.collect();
// 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);
ranges.sort_unstable_by_key(|&(s, _)| s);
let mut merged: Vec<(u32, u32)> = Vec::with_capacity(ranges.len());
if !ranges.is_empty() {
let mut iter = ranges.into_iter();
let (mut cur_s, mut cur_end) = iter.next().unwrap(); // cur_end exclusive
for (s, end_excl) in iter {
if s <= cur_end {
// overlap/adjacent -> extend
if end_excl > cur_end {
cur_end = end_excl;
}
} else {
// push disjoint segment as (start, len)
merged.push((cur_s, cur_end - cur_s)); // len = excl - start
cur_s = s;
cur_end = end_excl;
}
}
merged.push((cur_s, cur_end - cur_s));
}
// build prefix sums (prefix[0] = 0; prefix.len() == merged.len() + 1)
let prefix = merged.iter().fold(vec![0u32], |mut acc, (_, len)| {
let last = acc.last().unwrap();
acc.push(last.saturating_add(*len));
acc
});
// total is guaranteed > 0 by precondition (input.len() >= 1)
let total = *prefix.last().unwrap();
// pick step and seed
let step = pick_random_coprime(total);
let mut rng = rand::rng();
let first = rng.random_range(0..total);
Self {
active: true,
normalized_end,
total,
normalized_first_pick: first,
normalized_pick: first,
step,
normalized_first_pick,
normalized_pick: normalized_first_pick,
actual_start: start,
ranges: merged,
prefix,
serial_itr: None,
serial_seen: None,
}
}
/// Construct a serial iterator that yields ports in original input order,
/// skipping duplicates. The deduplication is done on the fly with a seen-table.
///
/// Preconditions:
/// - `input` must contain at least one `(u16,u16)` and each pair must satisfy `start <= end`.
pub fn new_serial(input: &[(u16, u16)]) -> Self {
// Build a serial iterator that yields ports in *input order* (start..=end).
// We keep the merged ranges/prefix empty here (they are not needed for serial mode).
let input = input.to_vec();
let serial_itr = input.into_iter().flat_map(|(start, end)| start..=end);
let serial_itr_boxed: Box<dyn Iterator<Item = u16>> = Box::new(serial_itr);
// One entry per possible port (0..=65535).
let seen = vec![false; usize::from(u16::MAX) + 1];
Self {
active: true,
total: 0,
normalized_first_pick: 0,
normalized_pick: 0,
step: 0,
ranges: Vec::new(),
prefix: Vec::new(),
serial_itr: Some(serial_itr_boxed),
serial_seen: Some(seen),
}
}
}
impl Iterator for RangeIterator {
type Item = u16;
// The next step is always bound by the formula: N+1 = (N + STEP) % TOP_OF_THE_RANGE
// It will only stop once we generate a number equal to the first generated number.
/// Advance the iterator by one port.
///
/// 1. Read the current normalized index `cur`.
/// 2. Compute `next = (cur + step) % total` and update `normalized_pick`.
/// 3. If `next == normalized_first_pick` mark `active = false` (we completed the cycle).
/// 4. Map the returned index `cur` into the merged ranges via the prefix array:
/// - find the range index `idx` where `prefix[idx] <= cur < prefix[idx+1]`,
/// - offset = `cur - prefix[idx]`,
/// - port = `ranges[idx].0 + offset`.
/// 5. Return `port as u16`.
///
fn next(&mut self) -> Option<Self::Item> {
if !self.active {
return None;
}
let current_pick = self.normalized_pick;
let next_pick = (current_pick + self.step) % self.normalized_end;
// SERIAL iterator fast-path: preserve original input order but skip duplicates.
if let (Some(it), Some(seen)) = (self.serial_itr.as_mut(), self.serial_seen.as_mut()) {
for p in it.by_ref() {
// Yield each port only the first time it is seen.
if !std::mem::replace(&mut seen[usize::from(p)], true) {
return Some(p);
}
// otherwise skip duplicate and continue
}
// serial iterator exhausted: drop it and mark inactive
self.serial_itr = None;
self.serial_seen = None;
self.active = false;
return None;
}
// If the next pick is equal to the first pick this means that
// we have iterated through the entire range.
if next_pick == self.normalized_first_pick {
// RANDOMIZED (LCG) path
let cur = self.normalized_pick;
let next = (cur + self.step) % self.total;
// if next equals the original seed we finished the cycle after returning cur
if next == self.normalized_first_pick {
self.active = false;
}
self.normalized_pick = next_pick;
Some(
(self.actual_start + current_pick)
.try_into()
.expect("Could not convert u32 to u16"),
)
self.normalized_pick = next;
// Map cur -> port using prefix + ranges (binary search)
let mut lo: usize = 0;
let mut hi: usize = self.ranges.len();
while lo < hi {
let mid = (lo + hi) / 2;
if self.prefix[mid + 1] > cur {
hi = mid;
} else {
lo = mid + 1;
}
}
let idx = lo;
let offset = cur - self.prefix[idx];
let (start, _len) = self.ranges[idx];
let port = (start + offset)
.try_into()
.expect("Could not convert u32 to u16");
Some(port)
}
}
@@ -83,15 +208,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;
} else {
candidate = rng.gen_range(lower_range, upper_range);
}
candidate = rng.random_range(lower_range..upper_range);
}
end - 1
@@ -99,36 +223,105 @@ fn pick_random_coprime(end: u32) -> u32 {
#[cfg(test)]
mod tests {
use super::RangeIterator;
use super::*;
use std::collections::HashSet;
// Helper: collect, sort and return ports produced by randomized RangeIterator
fn generate_sorted_from_ranges_random(input: &[(u16, u16)]) -> Vec<u16> {
let mut it = RangeIterator::new_random(input);
let mut v: Vec<u16> = it.by_ref().collect();
v.sort_unstable();
v
}
// Helper: collect, sort and return ports produced by serial RangeIterator
fn generate_sorted_from_ranges_serial(input: &[(u16, u16)]) -> Vec<u16> {
let mut it = RangeIterator::new_serial(input);
let mut v: Vec<u16> = it.by_ref().collect();
v.sort_unstable();
v
}
// Build expected sorted unique ports from input ranges (inclusive)
fn expected_ports_from_ranges(input: &[(u16, u16)]) -> Vec<u16> {
let mut s = HashSet::new();
for &(start, end) in input {
for p in start..=end {
s.insert(p);
}
}
let mut v: Vec<u16> = s.into_iter().collect();
v.sort_unstable();
v
}
#[test]
fn range_iterator_iterates_through_the_entire_range() {
let result = generate_sorted_range(1, 10);
let expected_range = (1..10).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
fn random_range_iterator_test() {
// small disjoint ranges
let input = &[(1u16, 10u16), (20u16, 30u16), (100u16, 110u16)];
let result = generate_sorted_from_ranges_random(input);
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, result);
let result = generate_sorted_range(1, 100);
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
// larger disjoint ranges
let input = &[(1u16, 100u16), (200u16, 500u16)];
let result = generate_sorted_from_ranges_random(input);
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, result);
let result = generate_sorted_range(1, 1000);
let expected_range = (1..1000).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
// overlapping and adjacent
let input = &[(10u16, 20u16), (15u16, 25u16), (26u16, 30u16)];
let result = generate_sorted_from_ranges_random(input);
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, result);
let result = generate_sorted_range(1, 65_535);
let expected_range = (1..65_535).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
// near-full domain (heavy): we only assert lengths & equality
let input = &[(1u16, 65_535u16)];
let result = generate_sorted_from_ranges_random(input);
let expected = expected_ports_from_ranges(input);
assert_eq!(expected.len(), result.len());
assert_eq!(expected, result);
let result = generate_sorted_range(1000, 2000);
let expected_range = (1000..2000).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
// multiple disjoint ranges - check dedupe & coverage
let input = &[(50u16, 100u16), (1000u16, 2000u16), (30000u16, 30010u16)];
let result = generate_sorted_from_ranges_random(input);
let set_len = result.iter().copied().collect::<HashSet<u16>>().len();
assert_eq!(set_len, result.len());
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, 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();
#[test]
fn serial_range_iterator_test() {
// serial should preserve input-order semantics but here we only assert
// coverage (no duplicates) by sorting results and comparing expected set.
result
// small disjoint ranges
let input = &[(1u16, 10u16), (20u16, 30u16), (100u16, 110u16)];
let result = generate_sorted_from_ranges_serial(input);
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, result);
// overlapping and adjacent
let input = &[(10u16, 20u16), (15u16, 25u16), (26u16, 30u16)];
let result = generate_sorted_from_ranges_serial(input);
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, result);
// multiple disjoint ranges
let input = &[(50u16, 100u16), (1000u16, 2000u16), (30000u16, 30010u16)];
let result = generate_sorted_from_ranges_serial(input);
let set_len = result.iter().copied().collect::<HashSet<u16>>().len();
assert_eq!(set_len, result.len());
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, result);
// all possible inputs
let input = &[(u16::MIN, u16::MAX)];
let result = generate_sorted_from_ranges_serial(input);
let set_len = result.iter().copied().collect::<HashSet<u16>>().len();
assert_eq!(set_len, result.len());
let expected = expected_ports_from_ranges(input);
assert_eq!(expected, result);
}
}
+408 -157
View File
@@ -1,20 +1,49 @@
use super::PortStrategy;
//! Core functionality for actual scanning behaviour.
use crate::generated::get_parsed_data;
use crate::port_strategy::PortStrategy;
use crate::tui::println_safe;
use log::debug;
mod socket_iterator;
use socket_iterator::SocketIterator;
use async_std::io;
use async_std::net::TcpStream;
use async_std::prelude::*;
use async_std::task::sleep;
use async_std::{io, net::UdpSocket};
use colored::Colorize;
use futures::stream::FuturesUnordered;
use std::collections::BTreeMap;
use std::{
collections::HashSet,
collections::{HashMap, HashSet},
net::{IpAddr, Shutdown, SocketAddr},
num::NonZeroU8,
sync::Arc,
time::Duration,
};
/// UDP payload lookup: port -> payload bytes
///
/// `get_parsed_data()` returns a `&'static BTreeMap<...>`, so we can store
/// references to the payload bytes without cloning them.
#[doc(hidden)]
pub type UdpPayloadLookup = HashMap<u16, &'static [u8]>;
#[doc(hidden)]
pub fn build_udp_payload_lookup(udp_map: &'static BTreeMap<Vec<u16>, Vec<u8>>) -> UdpPayloadLookup {
let mut lookup: UdpPayloadLookup = HashMap::new();
for (ports, payload_vec) in udp_map.iter() {
let payload: &'static [u8] = payload_vec.as_slice();
for &port in ports.iter() {
// Preserve existing behavior: if duplicates exist, last insert wins.
lookup.insert(port, payload);
}
}
lookup
}
/// The class for the scanner
/// IP is data type IpAddr and is the IP address
/// start & end is where the port scan starts and ends
@@ -25,23 +54,42 @@ use std::{
#[derive(Debug)]
pub struct Scanner {
ips: Vec<IpAddr>,
batch_size: u16,
batch_size: usize,
timeout: Duration,
tries: NonZeroU8,
greppable: bool,
port_strategy: PortStrategy,
accessible: bool,
exclude_ports: Vec<u16>,
udp: bool,
print_open_ports: bool,
report_closed: bool,
interval: Duration,
}
/// The outcome for a single socket, as returned by [`Scanner::run_with_status`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortStatus {
/// The TCP connection succeeded, or the UDP target answered.
Open(SocketAddr),
/// The target actively refused the TCP connection (for example with a
/// RST). Only reported when [`Scanner::with_closed_ports`] is enabled.
Closed(SocketAddr),
}
// Allowing too many arguments for clippy.
#[allow(clippy::too_many_arguments)]
impl Scanner {
pub fn new(
ips: &[IpAddr],
batch_size: u16,
batch_size: usize,
timeout: Duration,
tries: u8,
greppable: bool,
port_strategy: PortStrategy,
accessible: bool,
exclude_ports: Vec<u16>,
udp: bool,
) -> Self {
Self {
batch_size,
@@ -51,40 +99,138 @@ impl Scanner {
port_strategy,
ips: ips.iter().map(ToOwned::to_owned).collect(),
accessible,
exclude_ports,
udp,
print_open_ports: false,
report_closed: false,
interval: Duration::ZERO,
}
}
/// Enables the CLI's incremental open-port output.
///
/// Library callers are quiet by default and can inspect the sockets returned by [`Self::run`].
#[must_use]
pub fn with_open_port_output(mut self) -> Self {
self.print_open_ports = true;
self
}
/// Also reports TCP ports that actively refuse the connection, as
/// [`PortStatus::Closed`] from [`Self::run_with_status`].
///
/// Ports that time out are still treated as filtered and are not reported.
/// UDP scans never report closed ports.
#[must_use]
pub fn with_closed_ports(mut self) -> Self {
self.report_closed = true;
self
}
/// Waits `interval` after scanning one port (on every address) before
/// scanning the next port, for slow, low-noise scans.
///
/// Within a port, up to `batch_size` addresses are still scanned
/// concurrently. A zero interval (the default) scans all sockets in
/// batches without any delay.
#[must_use]
pub fn with_interval(mut self, interval: Duration) -> Self {
self.interval = interval;
self
}
/// 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 sockets.
pub async fn run(&self) -> Vec<SocketAddr> {
let ports: Vec<u16> = self.port_strategy.order();
let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports);
let mut open_sockets: Vec<SocketAddr> = Vec::new();
self.run_with_status()
.await
.into_iter()
.filter_map(|status| match status {
PortStatus::Open(socket) => Some(socket),
PortStatus::Closed(_) => None,
})
.collect()
}
/// Like [`Self::run`], but returns the status of every socket that gave a
/// definitive answer: open sockets, plus closed sockets when
/// [`Self::with_closed_ports`] is enabled.
pub async fn run_with_status(&self) -> Vec<PortStatus> {
let ports: Vec<u16> = self
.port_strategy
.order()
.iter()
.filter(|&port| !self.exclude_ports.contains(port))
.copied()
.collect();
let mut found_sockets: Vec<PortStatus> = Vec::new();
let mut errors: HashSet<String> = HashSet::new();
// Build UDP payload lookup once (only if we are scanning UDP).
// This avoids cloning a big map into every spawned future and turns
// payload selection from O(n) to O(1).
let udp_payloads: Option<Arc<UdpPayloadLookup>> = if self.udp {
Some(Arc::new(build_udp_payload_lookup(get_parsed_data())))
} else {
None
};
debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {}\nInterval between ports {:?}",
self.batch_size,
self.ips.len(),
ports.len(),
(self.ips.len() * ports.len()),
self.interval);
if self.interval.is_zero() {
let sockets = SocketIterator::new(&self.ips, &ports);
self.scan_sockets(sockets, &udp_payloads, &mut found_sockets, &mut errors)
.await;
} else {
// Scan one port (on every address) at a time and wait `interval`
// before moving on to the next port.
for (i, port) in ports.iter().enumerate() {
if i > 0 {
sleep(self.interval).await;
}
let sockets = SocketIterator::new(&self.ips, std::slice::from_ref(port));
self.scan_sockets(sockets, &udp_payloads, &mut found_sockets, &mut errors)
.await;
}
}
debug!("Typical socket connection errors {errors:?}");
debug!("Sockets found: {:?}", found_sockets);
found_sockets
}
/// Scans every socket yielded by `sockets`, keeping at most `batch_size`
/// connection attempts in flight.
async fn scan_sockets(
&self,
mut sockets: SocketIterator<'_>,
udp_payloads: &Option<Arc<UdpPayloadLookup>>,
found_sockets: &mut Vec<PortStatus>,
errors: &mut HashSet<String>,
) {
let mut ftrs = FuturesUnordered::new();
let mut errors: HashSet<String> = HashSet::with_capacity(self.ips.len() * 1000);
for _ in 0..self.batch_size {
if let Some(socket) = socket_iterator.next() {
ftrs.push(self.scan_socket(socket));
if let Some(socket) = sockets.next() {
ftrs.push(self.scan_socket(socket, udp_payloads.clone()));
} else {
break;
}
}
debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ",
self.batch_size,
self.ips.len(),
&ports.len(),
(self.ips.len() * ports.len()));
while let Some(result) = ftrs.next().await {
if let Some(socket) = socket_iterator.next() {
ftrs.push(self.scan_socket(socket));
if let Some(socket) = sockets.next() {
ftrs.push(self.scan_socket(socket, udp_payloads.clone()));
}
match result {
Ok(socket) => open_sockets.push(socket),
Ok(status) => found_sockets.push(status),
Err(e) => {
let error_string = e.to_string();
if errors.len() < self.ips.len() * 1000 {
@@ -93,58 +239,60 @@ impl Scanner {
}
}
}
debug!("Typical socket connection errors {:?}", errors);
debug!("Open Sockets found: {:?}", &open_sockets);
open_sockets
}
/// 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> {
let tries = self.tries.get();
async fn scan_socket(
&self,
socket: SocketAddr,
udp_payloads: Option<Arc<UdpPayloadLookup>>,
) -> io::Result<PortStatus> {
if self.udp {
return self.scan_udp_socket(socket, udp_payloads).await;
}
let tries = self.tries.get();
for nr_try in 1..=tries {
match self.connect(socket).await {
Ok(x) => {
debug!(
"Connection was successful, shutting down stream {}",
&socket
);
if let Err(e) = x.shutdown(Shutdown::Both) {
debug!("Shutdown stream error {}", &e);
}
if !self.greppable {
if self.accessible {
println!("Open {}", socket.to_string());
} else {
println!("Open {}", socket.to_string().purple());
}
Ok(tcp_stream) => {
debug!("Connection was successful, shutting down stream {}", socket);
if let Err(e) = tcp_stream.shutdown(Shutdown::Both) {
debug!("Shutdown stream error {}", e);
}
self.fmt_ports(socket);
debug!("Return Ok after {} tries", nr_try);
return Ok(socket);
debug!("Return Ok after {nr_try} tries");
return Ok(PortStatus::Open(socket));
}
Err(e) => {
// A refused connection is a definitive answer, so there is
// no point in retrying it.
if self.report_closed && e.kind() == io::ErrorKind::ConnectionRefused {
self.fmt_closed_port(socket);
return Ok(PortStatus::Closed(socket));
}
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(' ');
error_string.push_str(&socket.ip().to_string());
return Err(io::Error::new(io::ErrorKind::Other, error_string));
return Err(io::Error::other(error_string));
}
}
};
@@ -152,16 +300,43 @@ impl Scanner {
unreachable!();
}
async fn scan_udp_socket(
&self,
socket: SocketAddr,
udp_payloads: Option<Arc<UdpPayloadLookup>>,
) -> io::Result<PortStatus> {
let payload: &[u8] = udp_payloads
.as_ref()
.and_then(|m| m.get(&socket.port()).copied())
.unwrap_or(b"");
let tries = self.tries.get();
for _ in 1..=tries {
match self.udp_scan(socket, payload, self.timeout).await {
Ok(true) => return Ok(PortStatus::Open(socket)),
Ok(false) => continue,
Err(e) => return Err(e),
}
}
Err(io::Error::other(format!(
"UDP scan timed-out for all tries on socket {socket}"
)))
}
/// 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(
@@ -171,120 +346,196 @@ impl Scanner {
.await?;
Ok(stream)
}
/// Binds to a UDP socket so we can send and receive packets
/// # Example
///
/// ```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.udp_bind(socket);
/// // returns Result which is either Ok(stream) for port is open, or Err for port is closed.
/// // Timeout occurs after self.timeout seconds
/// ```
///
async fn udp_bind(&self, socket: SocketAddr) -> io::Result<UdpSocket> {
let local_addr = match socket {
SocketAddr::V4(_) => "0.0.0.0:0".parse::<SocketAddr>().unwrap(),
SocketAddr::V6(_) => "[::]:0".parse::<SocketAddr>().unwrap(),
};
UdpSocket::bind(local_addr).await
}
/// Performs a UDP scan on the specified socket with a payload and wait duration
/// # Example
///
/// ```compile_fail
/// # use std::net::{IpAddr, Ipv6Addr, SocketAddr};
/// # use std::time::Duration;
/// let port: u16 = 123;
/// // 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);
/// let payload = vec![0, 1, 2, 3];
/// let wait = Duration::from_secs(1);
/// let result = scanner.udp_scan(socket, payload, wait).await;
/// // returns Result which is either Ok(true) if response received, or Ok(false) if timed out.
/// // Err is returned for other I/O errors.
async fn udp_scan(
&self,
socket: SocketAddr,
payload: &[u8],
wait: Duration,
) -> io::Result<bool> {
match self.udp_bind(socket).await {
Ok(udp_socket) => {
let mut buf = [0u8; 1024];
udp_socket.connect(socket).await?;
udp_socket.send(payload).await?;
match io::timeout(wait, udp_socket.recv(&mut buf)).await {
Ok(size) => {
debug!("Received {size} bytes");
self.fmt_ports(socket);
Ok(true)
}
Err(e) => {
if e.kind() == io::ErrorKind::TimedOut {
Ok(false)
} else {
Err(e)
}
}
}
}
Err(e) => {
debug!("Error binding UDP socket: {e:?}");
Err(e)
}
}
}
/// Formats and prints the port status
fn fmt_ports(&self, socket: SocketAddr) {
if self.print_open_ports && !self.greppable {
if self.accessible {
println_safe(format_args!("Open {socket}"));
} else {
println_safe(format_args!("Open {}", socket.to_string().purple()));
}
}
}
/// Prints a closed port (CLI output only, never in greppable mode).
fn fmt_closed_port(&self, socket: SocketAddr) {
if self.print_open_ports && !self.greppable {
if self.accessible {
println_safe(format_args!("Closed {socket}"));
} else {
println_safe(format_args!("Closed {}", socket.to_string().red()));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{PortRange, ScanOrder};
use async_std::task::block_on;
use std::{net::IpAddr, time::Duration};
#[test]
fn scanner_runs() {
// Makes sure the program still runs and doesn't panic
use crate::input::{PortRanges, ScanOrder};
// These tests never open sockets: they only build a `Scanner` or inspect
// the payload table generated by build.rs.
fn test_scanner() -> Scanner {
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(
let strategy = PortStrategy::pick(&Some(PortRanges(vec![(1, 1)])), None, ScanOrder::Serial);
Scanner::new(
&addrs,
10,
1,
Duration::from_millis(100),
1,
true,
false,
strategy,
true,
);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
false,
Vec::new(),
false,
)
}
#[test]
fn ipv6_scanner_runs() {
// Makes sure the program still runs and doesn't panic
let addrs = vec!["::1".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 1,
end: 1_000,
};
let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn quad_zero_scanner_runs() {
let addrs = vec!["0.0.0.0".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 1,
end: 1_000,
};
let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn google_dns_runs() {
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
let range = PortRange {
start: 400,
end: 445,
};
let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn infer_ulimit_lowering_no_panic() {
// Test behaviour on MacOS where ulimit is not automatically lowered
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
// mac should have this automatically scaled down
let range = PortRange {
start: 400,
end: 600,
};
let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
let scanner = Scanner::new(
&addrs,
10,
Duration::from_millis(100),
1,
true,
strategy,
true,
);
block_on(scanner.run());
assert_eq!(1, 1);
#[test]
fn library_scanner_is_quiet_by_default() {
let scanner = test_scanner();
assert!(!scanner.print_open_ports);
}
#[test]
fn cli_can_enable_open_port_output() {
let scanner = test_scanner().with_open_port_output();
assert!(scanner.print_open_ports);
}
#[test]
fn closed_ports_are_not_reported_by_default() {
assert!(!test_scanner().report_closed);
}
#[test]
fn closed_port_reporting_is_opt_in() {
assert!(test_scanner().with_closed_ports().report_closed);
}
#[test]
fn no_interval_by_default() {
assert!(test_scanner().interval.is_zero());
}
#[test]
fn with_interval_sets_the_delay_between_ports() {
let scanner = test_scanner().with_interval(Duration::from_millis(250));
assert_eq!(scanner.interval, Duration::from_millis(250));
}
/// Regression test for https://github.com/bee-san/RustScan/issues/933:
/// the SNMP public-walk probe must be the exact 33-byte BER packet, with
/// the literal `public` community string intact. The old hexdigits-only
/// decoding mangled it into a 28-byte probe that agents never answered.
#[test]
fn udp_snmp_probe_bytes_match_nmap() {
let payload = get_parsed_data()
.iter()
.find(|(ports, _)| ports.contains(&161))
.map(|(_, payload)| payload)
.expect("no UDP payload registered for port 161");
let expected: Vec<u8> = vec![
0x30, 0x1f, 0x02, 0x01, 0x00, 0x04, 0x06, b'p', b'u', b'b', b'l', b'i', b'c', 0xa1,
0x12, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x07, 0x30, 0x05,
0x06, 0x01, 0x00, 0x05, 0x00,
];
assert_eq!(*payload, expected);
}
/// The SSDP probe mixes `\xNN` escapes, `\"` escapes and literal text
/// across two quoted segments: segments must decode and concatenate
/// with no separators.
#[test]
fn udp_ssdp_probe_decodes_escapes_and_literal_text() {
let payload = get_parsed_data()
.iter()
.find(|(ports, _)| ports.contains(&1900))
.map(|(_, payload)| payload)
.expect("no UDP payload registered for port 1900");
let expected =
b"M-SEARCH * HTTP/1.1\r\nHost: 239.255.255.250:1900\r\nMan: \"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n"
.to_vec();
assert_eq!(*payload, expected);
}
}
+105 -33
View File
@@ -1,55 +1,78 @@
use itertools::{iproduct, Product};
use std::iter::FusedIterator;
use std::net::{IpAddr, SocketAddr};
use std::slice;
/// An iterator that receives a slice of IPs and ports and returns a
/// [`SocketAddr`] for each IP/port pair until all combinations are exhausted.
///
/// The goal of this iterator is to walk every IP and port combination
/// *without* allocating a big intermediate buffer — the alternative would be
/// materialising a `Vec<SocketAddr>` of every pair up front.
///
/// # Ordering
///
/// The IP/port order is intentionally reversed inside `product_it`: we want
/// `iproduct!` to iterate *all IPs for one port* before advancing to the next
/// port ("hold the port, go through all the IPs, then advance the port").
///
/// # Example
///
/// `SocketIterator` lives in a private module, so this example is not run as
/// a doctest; `goes_through_every_ip_port_combination` checks the same order.
///
/// ```ignore
/// # use std::net::IpAddr;
/// let ips = [
/// "127.0.0.1".parse::<IpAddr>().unwrap(),
/// "192.168.0.1".parse::<IpAddr>().unwrap(),
/// ];
/// let ports = [80u16, 443];
///
/// let mut it = SocketIterator::new(&ips, &ports);
/// assert_eq!(it.next(), Some("127.0.0.1:80".parse().unwrap()));
/// assert_eq!(it.next(), Some("192.168.0.1:80".parse().unwrap()));
/// assert_eq!(it.next(), Some("127.0.0.1:443".parse().unwrap()));
/// assert_eq!(it.next(), Some("192.168.0.1:443".parse().unwrap()));
/// assert_eq!(it.next(), None);
/// ```
#[derive(Clone)]
pub struct SocketIterator<'s> {
// product_it is a cartesian product iterator over
// the slices of ports and IP addresses.
//
// The IP/port order is intentionally reversed here since we want
// the itertools::iproduct! macro below to generate the pairs with
// all the IPs for one port before moving on to the next one
// ("hold the port, go through all the IPs, then advance the port...").
// See also the comments in the iterator implementation for an example.
product_it:
Product<Box<std::slice::Iter<'s, u16>>, Box<std::slice::Iter<'s, std::net::IpAddr>>>,
// No boxing: the iterator owns the concrete slice iterators directly.
// This keeps `SocketIterator` allocation-free and makes `Clone` free too.
product_it: Product<slice::Iter<'s, u16>, slice::Iter<'s, IpAddr>>,
}
/// An iterator that receives a slice of IPs and ports and returns a Socket
/// for each IP and port pair until all of these combinations are exhausted.
/// The goal of this iterator is to go over every IP and port combination
/// wihout generating a big memory footprint. The alternative would be
/// generating a vector containing all these combinations.
impl<'s> SocketIterator<'s> {
pub fn new(ips: &'s [IpAddr], ports: &'s [u16]) -> Self {
let ports_it = Box::new(ports.iter());
let ips_it = Box::new(ips.iter());
Self {
product_it: iproduct!(ports_it, ips_it),
// `iproduct!` calls `.into_iter()` on each argument; `slice::Iter`
// is already an iterator, so this is a no-op (no allocation).
product_it: iproduct!(ports.iter(), ips.iter()),
}
}
}
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
/// IPs and ports or None when these combinations are exhausted. Every
/// IP will have the same port until a port is incremented.
///
/// let it = SocketIterator::new(&["127.0.0.1", "192.168.0.1"], &[80, 443]);
/// it.next(); // 127.0.0.1:80
/// it.next(); // 192.168.0.1:80
/// it.next(); // 127.0.0.1:443
/// it.next(); // 192.168.0.1:443
/// it.next(); // None
/// Returns the next socket, or `None` when all combinations are exhausted.
/// Every IP is paired with the same port until the port advances.
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))
}
// Delegate the size hint so callers like `collect()` can pre-size buffers.
fn size_hint(&self) -> (usize, Option<usize>) {
self.product_it.size_hint()
}
}
// `Product` and `slice::Iter` are both fused, so we are too.
impl FusedIterator for SocketIterator<'_> {}
#[cfg(test)]
mod tests {
use super::SocketIterator;
@@ -72,4 +95,53 @@ mod tests {
assert_eq!(Some(SocketAddr::new(addrs[1], ports[2])), it.next());
assert_eq!(None, it.next());
}
#[test]
fn size_hint_is_exact() {
let addrs = ["127.0.0.1".parse::<IpAddr>().unwrap()];
let ports: Vec<u16> = vec![22, 80, 443];
let mut it = SocketIterator::new(&addrs, &ports);
assert_eq!(it.size_hint(), (3, Some(3)));
it.next();
assert_eq!(it.size_hint(), (2, Some(2)));
}
#[test]
fn clone_resumes_independently() {
let addrs = [
"127.0.0.1".parse::<IpAddr>().unwrap(),
"192.168.0.1".parse::<IpAddr>().unwrap(),
];
let ports: Vec<u16> = vec![22, 80];
let mut a = SocketIterator::new(&addrs, &ports);
assert_eq!(a.next(), Some(SocketAddr::new(addrs[0], ports[0])));
let b = a.clone(); // b starts where a is now
// 2 IPs x 2 ports = 4 sockets, one of which was consumed before cloning.
let rest = [
SocketAddr::new(addrs[1], ports[0]),
SocketAddr::new(addrs[0], ports[1]),
SocketAddr::new(addrs[1], ports[1]),
];
assert_eq!(a.collect::<Vec<_>>(), rest);
assert_eq!(b.collect::<Vec<_>>(), rest);
}
#[test]
fn empty_inputs_yield_nothing() {
let addrs = ["127.0.0.1".parse::<IpAddr>().unwrap()];
let ports = [22u16];
for mut it in [
SocketIterator::new(&[], &ports),
SocketIterator::new(&addrs, &[]),
] {
assert_eq!(it.size_hint(), (0, Some(0)));
assert_eq!(it.next(), None);
// Stays exhausted, as promised by the `FusedIterator` impl.
assert_eq!(it.next(), None);
}
}
}
+197 -165
View File
@@ -1,131 +1,155 @@
//! 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
//!
//! Script file examples:
//! fixtures/test_script.py
//! fixtures/test_script.pl
//! fixtures/test_script.sh
//! fixtures/test_script.txt
//! - `fixtures/test_rustscan_scripts.toml`
//!
//! 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
//! Metadata-only test fixture:
//!
//! 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.
//! - `fixtures/.rustscan_scripts/test_script.txt`
//!
//! 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.
//! `call_format` in script files can be of 2 variants:
//!
//! 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;
use std::fs::{self, File};
use std::io::{self, prelude::*};
use std::net::IpAddr;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::string::ToString;
use subprocess::{Exec, ExitStatus};
use text_placeholder::Template;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;
static DEFAULT: &str = r#"tags = ["core_approved", "RustScan", "default"]
developer = [ "RustScan", "https://github.com/RustScan" ]
ports_separator = ","
call_format = "nmap -vvv -p {{port}} {{ip}}"
call_format = "nmap -vvv -p {{port}} -{{ipversion}} {{ip}}"
"#;
#[cfg(not(tarpaulin_include))]
pub fn init_scripts(scripts: ScriptsRequired) -> Result<Vec<ScriptFile>> {
pub fn init_scripts(scripts: &ScriptsRequired) -> Result<Vec<ScriptFile>> {
let mut scripts_to_run: Vec<ScriptFile> = Vec::new();
match scripts {
ScriptsRequired::None => Ok(scripts_to_run),
ScriptsRequired::None => {}
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 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 = match find_scripts(scripts_dir_base) {
Ok(script_paths) => script_paths,
Err(e) => return Err(anyhow!(e)),
};
debug!("Scripts paths \n{:?}", script_paths);
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 = match ScriptConfig::read_config() {
Ok(script_config) => script_config,
Err(e) => return Err(anyhow!(e)),
};
debug!("Script config \n{:?}", script_config);
debug!("Scripts parsed \n{parsed_scripts:?}");
// Only Scripts that contain all the tags found in ScriptConfig will be selected.
if script_config.tags.is_some() {
let config_hashset: HashSet<String> =
script_config.tags.unwrap().into_iter().collect();
for script in &parsed_scripts {
if script.tags.is_some() {
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());
if let Some(config_hashset) = script_config.tags {
for script in parsed_scripts {
if let Some(script_hashset) = &script.tags {
if script_hashset
.iter()
.all(|tag| config_hashset.contains(tag))
{
scripts_to_run.push(script);
} else {
debug!(
"\nScript tags does not match config tags {:?} {}",
&script_hashset,
script.path.clone().unwrap().display()
script_hashset,
script.path.unwrap().display()
);
}
}
}
}
debug!("\nScript(s) to run {:?}", scripts_to_run);
Ok(scripts_to_run)
debug!("\nScript(s) to run {scripts_to_run:?}");
}
}
Ok(scripts_to_run)
}
pub fn parse_scripts(scripts: Vec<PathBuf>) -> Vec<ScriptFile> {
let mut parsed_scripts: Vec<ScriptFile> = Vec::with_capacity(scripts.len());
for script in scripts {
debug!("Parsing script {}", &script.display());
debug!("Parsing script {}", script.display());
if let Some(script_file) = ScriptFile::new(script) {
parsed_scripts.push(script_file);
}
@@ -134,6 +158,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>,
@@ -162,12 +187,14 @@ struct ExecPartsScript {
script: String,
ip: String,
port: String,
ipversion: String,
}
#[derive(Serialize)]
struct ExecParts {
ip: String,
port: String,
ipversion: String,
}
impl Script {
@@ -194,7 +221,7 @@ impl Script {
// Some variables get changed before read, and compiler throws warning on warn(unused_assignments)
#[allow(unused_assignments)]
pub fn run(self) -> Result<String> {
debug!("run self {:?}", &self);
debug!("run self {:?}", self);
let separator = self.ports_separator.unwrap_or_else(|| ",".into());
@@ -222,51 +249,77 @@ impl Script {
script: self.path.unwrap().to_str().unwrap().to_string(),
ip: self.ip.to_string(),
port: ports_str,
ipversion: match &self.ip {
IpAddr::V4(_) => String::from("4"),
IpAddr::V6(_) => String::from("6"),
},
};
to_run = default_template.fill_with_struct(&exec_parts_script)?;
} else {
let exec_parts: ExecParts = ExecParts {
ip: self.ip.to_string(),
port: ports_str,
ipversion: match &self.ip {
IpAddr::V4(_) => String::from("4"),
IpAddr::V6(_) => String::from("6"),
},
};
to_run = default_template.fill_with_struct(&exec_parts)?;
}
debug!("\nScript format to run {}", to_run);
let arguments = shell_words::split(&to_run).expect("Failed to parse script arguments");
execute_script(arguments)
debug!("\nScript format to run {to_run}");
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);
match process.capture() {
Ok(c) => {
let es = match c.exit_status {
ExitStatus::Exited(c) => c.try_into().unwrap(),
ExitStatus::Signaled(c) => c.into(),
ExitStatus::Other(c) => c,
ExitStatus::Undetermined => -1,
fn execute_script(script: &str) -> Result<String> {
debug!("\nScript arguments {script}");
let (cmd, arg) = if cfg!(unix) {
("sh", "-c")
} else {
("cmd.exe", "/c")
};
match Command::new(cmd)
.args([arg, script])
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.output()
{
Ok(output) => {
let status = output.status;
let es = match status.code() {
Some(code) => code,
_ => {
#[cfg(unix)]
{
status.signal().unwrap()
}
#[cfg(windows)]
{
return Err(anyhow!("Unknown exit status"));
}
}
};
if es != 0 {
return Err(anyhow!("Exit code = {}", es));
}
Ok(c.stdout_str())
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
}
Err(error) => {
debug!("Command error {}", error.to_string());
debug!("Command error {error}",);
Err(anyhow!(error.to_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());
debug!("Scripts folder found {}", path.display());
let mut files_vec: Vec<PathBuf> = Vec::new();
for entry in fs::read_dir(path)? {
let entry = entry?;
@@ -293,33 +346,31 @@ 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 {
debug!("Failed to read file: {}", &real_path.display());
debug!("Failed to read file: {}", real_path.display());
return None;
}
debug!("ScriptFile {} lines\n{}", &real_path.display(), &lines_buf);
debug!("ScriptFile {} lines\n{}", real_path.display(), lines_buf);
match toml::from_str::<ScriptFile>(&lines_buf) {
Ok(mut parsed) => {
debug!("Parsed ScriptFile{} \n{:?}", &real_path.display(), &parsed);
debug!("Parsed ScriptFile{} \n{:?}", real_path.display(), parsed);
parsed.path = Some(real_path);
// parsed_scripts.push(parsed);
Some(parsed)
}
Err(e) => {
debug!("Failed to parse ScriptFile headers {}", e.to_string());
debug!("Failed to parse ScriptFile headers {e}");
None
}
}
@@ -331,14 +382,14 @@ 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))]
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");
@@ -350,27 +401,13 @@ impl ScriptConfig {
#[cfg(test)]
mod tests {
use super::{find_scripts, parse_scripts, Script, ScriptFile};
// 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
fn into_script(script_f: ScriptFile) -> Script {
Script::build(
script_f.path,
"127.0.0.1".parse().unwrap(),
vec![80, 8080],
script_f.port,
script_f.ports_separator,
script_f.tags,
script_f.call_format,
)
}
use super::*;
#[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);
assert_eq!(scripts.len(), 1);
}
#[test]
@@ -386,26 +423,6 @@ mod tests {
.unwrap();
}
#[test]
#[should_panic]
fn open_script_file_invalid_call_format() {
let mut script_f =
ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap();
script_f.call_format = Some("qwertyuiop".to_string());
let script: Script = into_script(script_f);
let _output = script.run().unwrap();
}
#[test]
#[should_panic]
fn open_script_file_missing_call_format() {
let mut script_f =
ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap();
script_f.call_format = None;
let script: Script = into_script(script_f);
let _output = script.run().unwrap();
}
#[test]
#[should_panic]
fn open_nonexisting_script_file() {
@@ -430,37 +447,52 @@ mod tests {
assert_eq!(script_f.ports_separator, Some(",".to_string()));
assert_eq!(
script_f.call_format,
Some("nmap -vvv -p {{port}} {{ip}}".to_string())
Some("fixture {{ip}} {{port}}".to_string())
);
}
#[test]
fn run_bash_script() {
let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.sh".into()).unwrap();
let script: Script = into_script(script_f);
let output = script.run().unwrap();
// output has a newline at the end by default, .trim() trims it
assert_eq!(output.trim(), "127.0.0.1 80,8080");
}
fn test_custom_directory_config() {
// Create test config
let config_str = r#"
tags = ["core_approved", "example"]
directory = "fixtures/.rustscan_scripts"
"#;
#[test]
fn run_python_script() {
let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.py".into()).unwrap();
let script: Script = into_script(script_f);
let output = script.run().unwrap();
// output has a newline at the end by default, .trim() trims it
let config: ScriptConfig = toml::from_str(config_str).unwrap();
assert_eq!(
output.trim(),
"Python script ran with arguments ['fixtures/.rustscan_scripts/test_script.py', '127.0.0.1', '80,8080']"
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 run_perl_script() {
let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.pl".into()).unwrap();
let script: Script = into_script(script_f);
let output = script.run().unwrap();
// 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");
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());
}
}
+87 -20
View File
@@ -1,21 +1,55 @@
//! Utilities for terminal output during scanning.
use std::io::Write;
/// Prints a single line to stdout without ever panicking.
///
/// `println!` panics when writing to stdout fails, which is fatal under the
/// `panic = "abort"` release profile. That includes the `BrokenPipe` error
/// produced when a downstream consumer (e.g. `rustscan -g ... | head`)
/// closes the pipe, so instead the process exits quietly when that happens.
/// Other output errors are reported on stderr before exiting.
///
/// All user-facing output should go through this function (the `warning!`,
/// `detail!`, `output!` and `funny_opening!` macros already do) instead of
/// `println!`.
pub fn println_safe(args: std::fmt::Arguments<'_>) {
match writeln!(std::io::stdout().lock(), "{args}") {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
// the reader end of the pipe is gone; nothing left to print to
std::process::exit(0);
}
Err(e) => {
let _ = writeln!(std::io::stderr(), "rustscan: failed writing to stdout: {e}");
std::process::exit(1);
}
}
}
/// 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);
$crate::tui::println_safe(format_args!(
"{} {}",
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);
$crate::tui::println_safe(format_args!("{}", $name));
} else {
println!("{} {}", Red.bold().paint("[!]"), $name);
$crate::tui::println_safe(format_args!(
"{} {}",
ansi_term::Colour::Red.bold().paint("[!]"),
$name
));
}
}
};
@@ -24,18 +58,24 @@ macro_rules! warning {
#[macro_export]
macro_rules! detail {
($name:expr) => {
use ansi_term::Colour::Blue;
println!("{} {}", Blue.bold().paint("[~]"), $name);
$crate::tui::println_safe(format_args!(
"{} {}",
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);
$crate::tui::println_safe(format_args!("{}", $name));
} else {
println!("{} {}", Blue.bold().paint("[~]"), $name);
$crate::tui::println_safe(format_args!(
"{} {}",
ansi_term::Colour::Blue.bold().paint("[~]"),
$name
));
}
}
};
@@ -44,18 +84,24 @@ macro_rules! detail {
#[macro_export]
macro_rules! output {
($name:expr) => {
use ansi_term::Colour::RGB;
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
$crate::tui::println_safe(format_args!(
"{} {}",
ansi_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);
$crate::tui::println_safe(format_args!("{}", $name));
} else {
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
$crate::tui::println_safe(format_args!(
"{} {}",
ansi_term::Colour::RGB(0, 255, 9).bold().paint("[>]"),
$name
));
}
}
};
@@ -65,7 +111,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🌍",
@@ -73,10 +119,31 @@ macro_rules! funny_opening {
"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.",
"RustScan: allowing you to send UDP packets into the void 1200x faster than NMAP",
"Port scanning: Because every port has a story to tell.",
"I scanned ports so fast, even my computer was surprised.",
"Scanning ports faster than you can say 'SYN ACK'",
"RustScan: Where '404 Not Found' meets '200 OK'.",
"RustScan: Exploring the digital landscape, one IP at a time.",
"TreadStone was here 🚀",
"With RustScan, I scan ports so fast, even my firewall gets whiplash 💨",
"Scanning ports so fast, even the internet got a speeding ticket!",
];
let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap();
let random_quote = quotes.choose(&mut rand::rng()).unwrap();
println!("{}\n", random_quote);
// println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
$crate::tui::println_safe(format_args!("{}\n", random_quote));
};
}
-109
View File
@@ -1,109 +0,0 @@
/*
* Test rustscan against different targets with a time limit.
* The tests assumes target/debug/rustscan has already been built.
*
* The tests are #[ignore] to avoid running them during normal development.
*
* Their tests in the timelimits module are run by travis during CI.
*/
use std::process::Command;
use std::time::Duration;
use wait_timeout::ChildExt;
const TIMEOUT_MARGIN: u32 = 3;
#[cfg(not(tarpaulin_include))]
fn run_rustscan_with_timeout(args: &[&str], timeout: Duration) {
println!("Running: target/debug/rustscan: {}", args.join(" "));
use std::time::Instant;
let start = Instant::now();
let mut child = Command::new("target/debug/rustscan")
.args(args)
.spawn()
.unwrap();
let mut tries = TIMEOUT_MARGIN;
loop {
match child.wait_timeout(timeout).unwrap() {
Some(_status) => break,
None => {
tries -= 1;
if tries == 0 {
// child hasn't exited yet
child.kill().unwrap();
child.wait().unwrap().code();
panic!("Timeout while running command");
}
}
}
}
let end = Instant::now();
let duration = end.saturating_duration_since(start).as_secs_f32();
println!("time: {:1.1}s", duration);
}
mod timelimits {
#[test]
#[ignore]
fn scan_localhost() {
let timeout = super::Duration::from_secs(25);
super::run_rustscan_with_timeout(&["--greppable", "--no-nmap", "127.0.0.1"], timeout);
}
#[test]
#[ignore]
fn scan_google_com() {
super::run_rustscan_with_timeout(
&[
"--greppable",
"--no-nmap",
"-u",
"5000",
"-b",
"2500",
"google.com",
],
super::Duration::from_secs(28),
);
}
#[test]
#[ignore]
fn scan_example_com() {
super::run_rustscan_with_timeout(
&[
"--greppable",
"--no-nmap",
"-u",
"5000",
"-b",
"2500",
"example.com",
],
super::Duration::from_secs(28),
);
}
#[test]
#[ignore]
fn scan_rustscan_cmnatic_co_uk() {
super::run_rustscan_with_timeout(
&[
"--greppable",
"--no-nmap",
"-u",
"5000",
"-b",
"2500",
"rustscan.cmnatic.co.uk",
],
super::Duration::from_secs(26),
);
}
}
+32
View File
@@ -0,0 +1,32 @@
use rustscan::generated::get_parsed_data;
use rustscan::scanner::build_udp_payload_lookup;
#[test]
fn udp_payload_lookup_contains_common_udp_ports() {
let udp_map = get_parsed_data();
let lookup = build_udp_payload_lookup(udp_map);
// These are common UDP services; the payload database should include them.
assert!(
lookup.contains_key(&53),
"expected UDP payload for DNS (53)"
);
assert!(
lookup.contains_key(&123),
"expected UDP payload for NTP (123)"
);
}
#[test]
fn udp_payload_lookup_payloads_are_non_empty_for_known_ports() {
let udp_map = get_parsed_data();
let lookup = build_udp_payload_lookup(udp_map);
// Don't assert exact bytes (the generated payload set may evolve),
// but it should not be empty for these well-known protocols.
let dns = lookup.get(&53).expect("missing DNS payload");
assert!(!dns.is_empty(), "DNS payload should not be empty");
let ntp = lookup.get(&123).expect("missing NTP payload");
assert!(!ntp.is_empty(), "NTP payload should not be empty");
}