diff --git a/.SRCINFO b/.SRCINFO index 7a4b6f3..2e95ced 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -2,7 +2,7 @@ pkgbase = rustscan-bin pkgdesc = Faster Nmap Scanning with Rust pkgver = 1.1.0 pkgrel = 1 - url = https://github.com/brandonskerritt/rustscan + url = https://github.com/rustscan/rustscan arch = x86_64 license = MIT depends = rustup diff --git a/.all-contributorsrc b/.all-contributorsrc index c2996d5..8163ae9 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -86,6 +86,15 @@ "code" ] }, + { + "login": "TGotwig", + "name": "Thomas Gotwig", + "avatar_url": "https://avatars0.githubusercontent.com/u/30773779?v=4", + "profile": "https://tgotwig.me", + "contributions": [ + "platform" + ] + }, { "login": "remigourdon", "name": "Rémi Gourdon", @@ -95,6 +104,161 @@ "doc", "code" ] + }, + { + "login": "cmnatic", + "name": "Ben (CMNatic)", + "avatar_url": "https://avatars3.githubusercontent.com/u/4163116?v=4", + "profile": "https://cmnatic.co.uk", + "contributions": [ + "code", + "doc", + "design" + ] + }, + { + "login": "Ferryistaken", + "name": "Alessandro Ferrari", + "avatar_url": "https://avatars3.githubusercontent.com/u/47927670?v=4", + "profile": "https://github.com/Ferryistaken", + "contributions": [ + "content" + ] + }, + { + "login": "Phenomite", + "name": "Phenomite", + "avatar_url": "https://avatars2.githubusercontent.com/u/8285537?v=4", + "profile": "https://github.com/Phenomite", + "contributions": [ + "content" + ] + }, + { + "login": "SuperSandro2000", + "name": "Sandro", + "avatar_url": "https://avatars2.githubusercontent.com/u/7258858?v=4", + "profile": "https://supersandro.de/", + "contributions": [ + "content", + "bug", + "code" + ] + }, + { + "login": "caass", + "name": "Cass", + "avatar_url": "https://avatars2.githubusercontent.com/u/25358963?v=4", + "profile": "https://swag.lgbt", + "contributions": [ + "platform", + "code", + "bug" + ] + }, + { + "login": "niklasmohrin", + "name": "Niklas Mohrin", + "avatar_url": "https://avatars0.githubusercontent.com/u/47574893?v=4", + "profile": "https://github.com/niklasmohrin", + "contributions": [ + "doc", + "code", + "bug" + ] + }, + { + "login": "tim77", + "name": "Artem Polishchuk", + "avatar_url": "https://avatars0.githubusercontent.com/u/5614476?v=4", + "profile": "https://liberapay.com/Artem4/", + "contributions": [ + "platform" + ] + }, + { + "login": "buermarc", + "name": "buermarc", + "avatar_url": "https://avatars2.githubusercontent.com/u/44375277?v=4", + "profile": "https://github.com/buermarc", + "contributions": [ + "code" + ] + }, + { + "login": "bergabman", + "name": "bergabman", + "avatar_url": "https://avatars1.githubusercontent.com/u/44554109?v=4", + "profile": "https://github.com/bergabman", + "contributions": [ + "code", + "bug", + "design" + ] + }, + { + "login": "dmitris", + "name": "Dmitry Savintsev", + "avatar_url": "https://avatars0.githubusercontent.com/u/31205?v=4", + "profile": "https://github.com/dmitris", + "contributions": [ + "code" + ] + }, + { + "login": "bofh69", + "name": "Sebastian Andersson", + "avatar_url": "https://avatars3.githubusercontent.com/u/1444315?v=4", + "profile": "https://github.com/bofh69", + "contributions": [ + "code" + ] + }, + { + "login": "mattcorbin", + "name": "Matt Corbin", + "avatar_url": "https://avatars3.githubusercontent.com/u/6537765?v=4", + "profile": "https://github.com/mattcorbin", + "contributions": [ + "code" + ] + }, + { + "login": "rootsploit", + "name": "RootSploit", + "avatar_url": "https://avatars2.githubusercontent.com/u/67270834?v=4", + "profile": "http://rootsploit.com", + "contributions": [ + "blog" + ] + }, + { + "login": "eiffel-fl", + "name": "eiffel-fl", + "avatar_url": "https://avatars2.githubusercontent.com/u/12171754?v=4", + "profile": "https://github.com/eiffel-fl", + "contributions": [ + "code" + ] + }, + { + "login": "u5surf", + "name": "Y.Horie", + "avatar_url": "https://avatars1.githubusercontent.com/u/14180225?v=4", + "profile": "https://github.com/u5surf", + "contributions": [ + "code" + ] + }, + { + "login": "okrplay", + "name": "Oskar", + "avatar_url": "https://avatars3.githubusercontent.com/u/32576280?v=4", + "profile": "https://github.com/okrplay", + "contributions": [ + "code", + "test" + ] } ], "contributorsPerLine": 7, diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..331d387 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +Dockerfile diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 961edca..4ffe23f 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1 @@ -github: brandonskerritt -patreon: brandonskerritt -ko_fi: hacker -custom: ["https://monzo.me/bee", "https://paypal.me/brandonskerritt"] +github: bee-san diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..27bcee3 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,4 @@ +template: | + ## What’s Changed + + $CHANGES diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index a103595..d6c59c7 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -13,9 +13,9 @@ jobs: profile: minimal toolchain: stable override: true - - uses: actions-rs/cargo@v1 - with: - command: check + # - uses: actions-rs/cargo@v1 + # with: + # command: check test: name: Test Suite @@ -46,10 +46,3 @@ jobs: with: command: fmt args: --all -- --check - - links: - name: markdown-link-check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@v1 diff --git a/.github/workflows/markdown_link_check_9am_daily.yml b/.github/workflows/markdown.yml similarity index 100% rename from .github/workflows/markdown_link_check_9am_daily.yml rename to .github/workflows/markdown.yml diff --git a/.gitignore b/.gitignore index 7e7a411..f53f6f1 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,80 @@ pkg/ # End of https://www.toptal.com/developers/gitignore/api/rust,archlinuxpackages *.deb + + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +.idea/* +.idea +.idea* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5bcd528 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/rustscan_scripting_engine"] + path = src/rustscan_scripting_engine + url = https://github.com/RustScan/rustscan_scripting_engine diff --git a/.travis.yml b/.travis.yml index a4678e1..607f4c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,56 +1,38 @@ language: rust - os: - - linux - - osx - # - windows - +- linux +- osx arch: - - amd64 - - arm64 - +- amd64 +- arm64 services: - - docker - +- docker before_install: - rustup component add rustfmt - -# cargo test rust: - - stable - - beta +- stable +- beta jobs: fast_finish: true - -# Cargo fmt script: - cargo build - cargo test +- "ulimit -n 5000; cargo test timelimits:: -- --ignored --test-threads=1 --show-output" - cargo fmt -- --check - -# automatic deployment to cargo on release -# deploy: - -# provider: releases -# api_key: "GITHUB OAUTH TOKEN" -# file: "FILE TO UPLOAD" -# skip_cleanup: true -# on: -# tags: true - -# after_success: -# - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh -# - chmod +x send.sh -# - ./send.sh success $WEBHOOK_URL - - # only deploy on master branch - # on: - # branch: master - # provider: cargo - # token: $crates - - after_failure: - - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh - - chmod +x send.sh - - ./send.sh failure $WEBHOOK_URL +- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh +- chmod +x send.sh +- "./send.sh failure $WEBHOOK_URL" +after_success: | + if [ -n "${GITHUB_TOKEN}" ]; then + [ "${TRAVIS_BRANCH} " = master ] && + [ "${TRAVIS_PULL_REQUEST}" = false ] && + cargo doc && + echo "" > target/doc/index.html && + sudo pip install ghp-import && + ghp-import -n target/doc && + git push -fq https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages + fi +env: + global: + secure: Gf24LG7Hr3Q0drWyPrvISTL35Z57DxdS/wIOo7v+ZdauzY3Qjvv3Lh4TY//C4sdN0g1CLj4yjg7LQOqF4XRdh2Qah7JZPv/i2B5uz9nwjTsoRzmmMWML9VtfT1u53nSFqsNeXbvpmoNG9rfMXcoiyd+Lv+EgniHphDEBlXvS9n9KMcpOi1F2x7bsH0EK37wQYKMTpCCd5TPUPdZou7BJNiZS3ikRLpTRFF7uIN4gYtRH9kU5uBSphWgnJ6eBm2ItzZcUPOgrjAoF4RkmZAzGG4idDh1Qkf3No3nZwT3U5NSk8h7owtJ4oADlRgYqYKCB4YcC9cAKCJgDsyMlKKMkEyagvw1seFnBLCY3ljsW4EklpVEZ0rVqZOx4pTkIaxIxytLTC9pHmdDSnhdhFknOcVAQQWjg+nAZfD07QUeAqUdr0i/vxQoZitvbNY0znzEarycE1waKvwlkiB4gt385U1CBfYldydnOnRKkdtLNrCLClu13HU0djmSXMhojQJhTuNxTHtZAf5fKcWIWwkVbNZ5MNKYjQh4rKufqW3o1iHphB8dfAGsDEPv0RQMtVbu6H4+clCouLkFlxHOIrrOJ5SXQ8cIATPhnJR7y4cSDdRaN0DRLRjeLAIKlbOGBtNuFC+BftocaTY5qN42aKAMqQcT30ycfiLuRVJNiQJ5pkto= diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 02711ab..e2978f4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation. Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting ## Our Responsibilities @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at brandon@skerritt.blog. All +reported by contacting the project team at our discord via a private message to any of the owners. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. @@ -67,10 +67,10 @@ members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +This Code of Conduct is adapted from the Contributor Covenant homepage, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html -[homepage]: https://www.contributor-covenant.org +homepage: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq diff --git a/Cargo.lock b/Cargo.lock index 63befeb..720e082 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,21 +1,29 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] -name = "aho-corasick" -version = "0.7.13" +name = "addr2line" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "memchr", + "gimli", ] [[package]] -name = "ansi_term" -version = "0.11.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ - "winapi", + "memchr", ] [[package]] @@ -24,26 +32,20 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] -name = "arrayref" -version = "0.3.6" +name = "anyhow" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] name = "async-channel" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43de69555a39d52918e2bc33a408d3c0a86c829b212d898f4ca25d21a6387478" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" dependencies = [ "concurrent-queue", "event-listener", @@ -51,33 +53,115 @@ dependencies = [ ] [[package]] -name = "async-std" -version = "1.6.2" +name = "async-executor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d68a33ebc8b57800847d00787307f84a562224a14db069b0acefe4c2abbf5d" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" dependencies = [ "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-mutex", + "blocking", + "futures-lite", + "num_cpus", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2 0.4.4", + "waker-fn", + "winapi 0.3.9", +] + +[[package]] +name = "async-lock" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", + "futures-lite", + "gloo-timers", "kv-log-macro", "log", "memchr", "num_cpus", "once_cell", - "pin-project-lite", + "pin-project-lite 0.2.8", "pin-utils", "slab", - "smol", "wasm-bindgen-futures", ] [[package]] name = "async-task" -version = "3.0.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3" +checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" + +[[package]] +name = "async-trait" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "atomic-waker" @@ -93,14 +177,29 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] name = "autocfg" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "base64" @@ -110,52 +209,47 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "blake2b_simd" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blocking" -version = "0.4.7" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2468ff7bf85066b4a3678fede6fe66db31846d753ff0adfbfab2c6a6e81612b" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" dependencies = [ "async-channel", + "async-task", "atomic-waker", + "fastrand", "futures-lite", "once_cell", - "parking", - "waker-fn", ] [[package]] name = "bumpalo" -version = "3.4.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "cache-padded" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cc" -version = "1.0.58" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -164,12 +258,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "clap" -version = "2.33.2" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cidr-utils" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aee37eff22e119e0acb33e6e81f85d87a9abf612b82efbcb5fba31b6a74554fb" dependencies = [ - "ansi_term 0.11.0", + "debug-helper", + "num-bigint", + "num-traits", + "once_cell", + "regex", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", "atty", "bitflags", "strsim", @@ -186,7 +299,7 @@ checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" dependencies = [ "atty", "lazy_static", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -204,48 +317,75 @@ dependencies = [ "cache-padded", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ - "autocfg", - "cfg-if", + "cfg-if 1.0.0", "lazy_static", ] [[package]] -name = "dirs" -version = "3.0.1" +name = "ctor" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "enum-as-inner" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "atty", "humantime", @@ -256,21 +396,50 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.3.2" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68082183f458867ce7cddea16d4df4443c1537112c0c09c450dedc09daf5c719" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" [[package]] name = "fastrand" -version = "1.3.3" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a9cb09840f81cd211e435d00a4e487edd263dc3c8ff815c32dd76ad668ebed" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" dependencies = [ "futures-channel", "futures-core", @@ -283,9 +452,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" dependencies = [ "futures-core", "futures-sink", @@ -293,15 +462,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" [[package]] name = "futures-executor" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" dependencies = [ "futures-core", "futures-task", @@ -310,32 +479,31 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" [[package]] name = "futures-lite" -version = "0.1.10" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe71459749b2e8e66fb95df721b22fa08661ad384a0c5b519e11d3893b4692a" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" dependencies = [ "fastrand", "futures-core", "futures-io", "memchr", "parking", - "pin-project-lite", + "pin-project-lite 0.2.8", "waker-fn", ] [[package]] name = "futures-macro" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -343,24 +511,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" [[package]] name = "futures-task" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" -dependencies = [ - "once_cell", -] +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" [[package]] name = "futures-util" -version = "0.3.5" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" dependencies = [ "futures-channel", "futures-core", @@ -369,66 +534,176 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project", + "pin-project-lite 0.2.8", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] [[package]] name = "gcd" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7cd301bf2ab11ae4e5bdfd79c221d97a25e46c089144a62ee9d09cb32d2b92" +checksum = "f37978dab2ca789938a83b2f8bc1ef32db6633af9051a6cd409eff72cbaaa79a" +dependencies = [ + "paste", +] [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "gloo-timers" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", ] [[package]] name = "heck" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] [[package]] -name = "hermit-abi" -version = "0.1.15" +name = "heck" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] -name = "humantime" -version = "1.3.0" +name = "hostname" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ - "quick-error", + "libc", + "match_cfg", + "winapi 0.3.9", ] [[package]] -name = "js-sys" -version = "0.3.44" +name = "humantime" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipconfig" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +dependencies = [ + "socket2 0.3.19", + "widestring", + "winapi 0.3.9", + "winreg", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -446,72 +721,188 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.74" +version = "0.2.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" +checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "log" -version = "0.4.11" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", + "value-bag", ] [[package]] -name = "memchr" -version = "2.3.3" +name = "lru-cache" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "miniz_oxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", ] [[package]] -name = "once_cell" -version = "1.4.0" +name = "object" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" +checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "parking" -version = "1.0.6" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb300f271742d4a2a66c01b6b2fa0c83dfebd2e0bf11addb879a3547b4ed87c" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" [[package]] -name = "pin-project" -version = "0.4.23" +name = "paste" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" -dependencies = [ - "pin-project-internal", -] +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] -name = "pin-project-internal" -version = "0.4.23" +name = "percent-encoding" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project-lite" -version = "0.1.7" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" [[package]] name = "pin-utils" @@ -520,10 +911,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "ppv-lite86" -version = "0.2.8" +name = "polling" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "log", + "wepoll-ffi", + "winapi 0.3.9", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro-error" @@ -549,23 +953,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" - -[[package]] -name = "proc-macro-nested" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" - [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] @@ -578,9 +970,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.7" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -591,7 +983,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", "rand_chacha", "rand_core", @@ -614,7 +1006,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", ] [[package]] @@ -628,131 +1020,215 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] [[package]] name = "redox_users" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom", + "getrandom 0.2.6", "redox_syscall", - "rust-argon2", + "thiserror", ] [[package]] name = "regex" -version = "1.3.9" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.18" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] [[package]] name = "rlimit" -version = "0.3.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d26e65e82a1e2628c5f209ec12f4508fa50e644bd2f264138e60129d61eae8" +checksum = "81a9ed03edbed449d6897c2092c71ab5f7b5fb80f6f0b1a3ed6d40a6f9fc0720" dependencies = [ - "cfg-if", "libc", ] [[package]] -name = "rust-argon2" -version = "0.7.0" +name = "rustc-demangle" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustls" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" dependencies = [ "base64", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", + "log", + "ring", + "sct", + "webpki", ] [[package]] name = "rustscan" -version = "1.7.1" +version = "2.0.0" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", + "anyhow", "async-std", + "cidr-utils", "colored", "colorful", "dirs", "env_logger", "futures", "gcd", + "itertools", "log", "rand", "rlimit", + "serde", + "serde_derive", "shell-words", "structopt", + "subprocess", + "text_placeholder", + "toml", + "trust-dns-resolver", + "wait-timeout", ] [[package]] -name = "scoped-tls" -version = "1.0.0" +name = "ryu" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] [[package]] name = "shell-words" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "slab" -version = "0.4.2" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] -name = "smol" -version = "0.1.18" +name = "smallvec" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ - "async-task", - "blocking", - "concurrent-queue", - "fastrand", - "futures-io", - "futures-util", + "cfg-if 1.0.0", "libc", - "once_cell", - "scoped-tls", - "slab", - "socket2", - "wepoll-sys-stjepang", - "winapi", + "winapi 0.3.9", ] [[package]] name = "socket2" -version = "0.3.12" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ - "cfg-if", "libc", - "redox_syscall", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "strsim" version = "0.8.0" @@ -761,9 +1237,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.16" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5472fb24d7e80ae84a7801b7978f95a19ec32cb1876faea59ab711eb901976" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ "clap", "lazy_static", @@ -772,11 +1248,11 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.9" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro-error", "proc-macro2", "quote", @@ -784,10 +1260,20 @@ dependencies = [ ] [[package]] -name = "syn" -version = "1.0.38" +name = "subprocess" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" +checksum = "055cf3ebc2981ad8f0a5a17ef6652f652d87831f79fddcba2ac57bcb9a0aa407" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" dependencies = [ "proc-macro2", "quote", @@ -796,13 +1282,23 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] +[[package]] +name = "text_placeholder" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd6afcbe8d748e35406f4c3a79b60567a5104b451f1b618097f139294969ef4" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -813,31 +1309,195 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "1.0.1" +name = "thiserror" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" +dependencies = [ + "bytes", + "iovec", "lazy_static", + "memchr", + "mio", + "pin-project-lite 0.1.12", + "slab", +] + +[[package]] +name = "tokio-rustls" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" +dependencies = [ + "futures-core", + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "trust-dns-proto" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9" +dependencies = [ + "async-trait", + "backtrace", + "cfg-if 1.0.0", + "enum-as-inner", + "futures", + "idna", + "lazy_static", + "log", + "rand", + "smallvec", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb" +dependencies = [ + "cfg-if 0.1.10", + "futures", + "ipconfig", + "lazy_static", + "log", + "lru-cache", + "resolv-conf", + "rustls", + "smallvec", + "thiserror", + "tokio", + "tokio-rustls", + "trust-dns-proto", + "trust-dns-rustls", + "webpki-roots", +] + +[[package]] +name = "trust-dns-rustls" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7090b0e15f5b3ea3b3066a93ee71d18691d903530b1042fbe7412021491e2134" +dependencies = [ + "futures", + "log", + "rustls", + "tokio", + "tokio-rustls", + "trust-dns-proto", + "webpki", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" +dependencies = [ + "ctor", + "version_check", +] [[package]] name = "vec_map" @@ -847,15 +1507,24 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] [[package]] name = "waker-fn" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "wasi" @@ -864,20 +1533,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] -name = "wasm-bindgen" -version = "0.2.67" +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.67" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" dependencies = [ "bumpalo", "lazy_static", @@ -890,11 +1565,11 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.17" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95f8d235a77f880bcef268d379810ea6c0af2eacfa90b1ad5af731776e0c4699" +checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -902,9 +1577,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.67" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -912,9 +1587,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.67" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" dependencies = [ "proc-macro2", "quote", @@ -925,29 +1600,60 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.67" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "web-sys" -version = "0.3.44" +version = "0.3.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda38f4e5ca63eda02c059d243aa25b5f35ab98451e518c51612cd0f1bd19a47" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "wepoll-sys-stjepang" -version = "1.0.6" +name = "webpki" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd319e971980166b53e17b1026812ad66c6b54063be879eb182342b55284694" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739" +dependencies = [ + "webpki", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" dependencies = [ "cc", ] +[[package]] +name = "widestring" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -958,6 +1664,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -970,7 +1682,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -978,3 +1690,22 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/Cargo.toml b/Cargo.toml index cd2a712..a420056 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustscan" -version = "1.7.1" +version = "2.1.0" authors = ["Bee "] edition = "2018" description = "Faster Nmap Scanning with Rust" @@ -10,24 +10,43 @@ license = "MIT" keywords = ["port", "scanning", "nmap"] categories = ["command-line-utilities"] readme="README.md" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +exclude = [ + ".github/*", + "pictures/*", + "rustscan-debbuilder/*", +] [dependencies] colored = "2.0.0" -structopt = "0.3.16" -async-std = "1.6.2" +structopt = "0.3.20" +async-std = "1.7.0" futures = "0.3" -rlimit = "0.3.0" +rlimit = "0.5.4" shell-words = "1.0.0" log = "0.4.0" -env_logger = "0.7.1" +env_logger = "0.8.2" dirs = "3.0.1" gcd = "2.0.1" rand = "0.7.3" colorful = "0.2.1" ansi_term = "0.12.1" +toml = "0.5.7" +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"] } +anyhow = "1.0.40" +subprocess = "0.2.6" +text_placeholder = { version = "0.4", features = ["struct_context"] } + +[dev-dependencies] +wait-timeout = "0.2" [package.metadata.deb] depends = "$auto, nmap" section = "rust" + +[profile.release] +lto = true +panic = 'abort' diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..056e847 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM rust:alpine as builder +LABEL maintainer="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 " +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" ] diff --git a/Makefile b/Makefile index 4e5667a..1aeb16a 100644 --- a/Makefile +++ b/Makefile @@ -10,12 +10,12 @@ build: build-linux: @echo 'Building for Linux... 🐧' cross build --release --target=x86_64-unknown-linux-musl - mkdir -p target/release-archives && tar -czf target/release-archives/rustscan-linux.tar.gz target/x86_64-unknown-linux-musl/release/rustscan + mkdir -p target/release-archives && tar -C target/x86_64-unknown-linux-musl/release -czf target/release-archives/rustscan-linux.tar.gz rustscan build-mac: @echo 'Building for MacOS... 🍏' cross build --release --target=x86_64-apple-darwin - mkdir -p target/release-archives && tar -czf target/release-archives/rustscan-mac.tar.gz target/x86_64-apple-darwin/release/rustscan + mkdir -p target/release-archives && tar -C target/x86_64-apple-darwin/release -czf target/release-archives/rustscan-mac.tar.gz rustscan shasum: shasum -a 256 target/release-archives/rustscan-*.tar.gz diff --git a/README.md b/README.md index bf5cab9..0a64a05 100644 --- a/README.md +++ b/README.md @@ -1,359 +1,145 @@

➡️ -Discord | - Installation Guide | +Discord | + Installation Guide | Usage Guide ⬅️
- +

- Turns a 17 minutes Nmap scan into 19 seconds.
Find all open ports fast with RustScan, automatically pipe them into Nmap. + The Modern Port Scanner.
Fast, smart, effective.

-AUR version +AUR version -GitHub commit activity GitHub All Releases Crates.io -Discord +Discord Actions


-|

🐋 Docker (Recommended)

|

👩‍💻 Kali / Debian

|

🏗️ Arch

|

🔧 Cargo (Universal)

| -| ---- | ---- | ---- | --- | -|

|

|

|

-| `docker pull cmnatic/rustscan:debian-buster`

[Usage](https://github.com/RustScan/RustScan#docker-whale) | [Read the install guide](https://github.com/brandonskerritt/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) | `yay -S rustscan` | `cargo install rustscan` +|

🐋 Docker (Recommended)

|

👩‍💻 Kali / Debian

|

🏗️ Arch

|

🔧 Homebrew

| +| ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | +|

|

|

|

| +| `docker pull rustscan/rustscan:2.0.0`

[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` |


# 🤔 What is this? -1. Find ports quickly using Rustscan (**3 seconds at its fastest**). -2. Automatically runs `nmap` on those ports. -3. ??? -4. Profit! - - - - - - - - - - - - - - - - -
Name⚡ Nmap with RustScan ⚡ 🐢 Nmap 🐢
GifThe guy she tells you not to worry aboutYou
Time39 seconds17 minutes and 41 seconds
+![fast](pictures/fast.gif) -Note This is an older gif. RustScan's current top speed is 3 seconds for all 65k ports. This gif is 26 seconds.
- -RustScans **only** job is to reduce the friction between finding open ports and inputting them into nmap. +The Modern Port Scanner. **Find ports quickly (3 seconds at its fastest)**. Run scripts through our scripting engine (Python, Lua, Shell supported). # ✨ Features -* Scans all 65k ports in **8 seconds** (on 10k batch size). -* Saves you time by automatically piping it into Nmap. No more manual copying and pasting! -* Does one thing and does it well. **Only** purpose is to improve Nmap, not replace it! -* Let's you choose what Nmap commands to run, or uses the default. -* IPv6 Support + +- Scans all 65k ports in **3 seconds**. +- Full scripting engine support. Automatically pipe results into Nmap, or use our scripts (or write your own) to do whatever you want. +- Adaptive learning. RustScan improves the more you use it. No bloated machine learning here, just basic maths. +- The usuals you would expect. IPv6, CIDR, file input and more. +- Automatically pipes ports into Nmap. ## ‼️ Important Links -| Installation Guide | Documentation | Discord | -| ------------------ | ------------- | ------- | -| 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://github.com/RustScan/RustScan/issues/89) | 🦜 [Discord](https://discord.gg/GFrQsGy) +| 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) | ## 🙋 Table of Contents -* 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) -* 🐋 [Docker Usage](https://github.com/RustScan/RustScan#docker-whale) -* 🦜 [Discord](https://discord.gg/GFrQsGy) -* 🤸 [Usage](https://github.com/RustScan/RustScan#-usage) -* 🎪 [Community](https://github.com/RustScan/RustScan#-community) + +- 📖 [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) # 🔭 Why RustScan? -Why spend time running fast scans and manually copying the ports, or waiting for a 20 minute scan to finish when you can just do all 65k ports in less than a minute? -![gif](pictures/8seconds.gif) +RustScan is a modern take on the port scanner. Sleek & fast. All while providing extensive extendability to you. -**RustScan running in 8 seconds and finding all open ports out of 65k.** +Not to mention RustScan uses Adaptive Learning to improve itself over time, making it the best port scanner for **you**. -## 📊 RustScan vs Nmap vs MassScan +## 🧋 Speed -| **Name** | RustScan | Nmap | Masscan | -| ------------------------------------------------------------------------------------------ | -------- | ---- | ------- | -| Fast | ✅ | ❌ | ✅ | -| Actually useful | ❌ | ✅ | ❌ | -| Realises it's not useful, and pipes the only useful data into the only useful port scanner | ✅ | ❌ | ❌ | +![fast](pictures/fast.gif) + +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. + +[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?". + +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. + +[Read more here](https://github.com/RustScan/RustScan/wiki/Increasing-Speed-&-Accuracy). + +## ⚙️ Extensible + +![scripts](pictures/scripts.gif) + +_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. + +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). + +## 🌊 Adaptive + +![adaptive](pictures/adaptive.gif) + +_RustScan automatically fine-tuning 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). + +## 👩‍🦯 Accessible + +![fast](pictures/accessible.gif) + +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. + +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 need Nmap**. If you have Kali Linux or Parrot OS installed, you already have Nmap. If not, [follow the nmap install guide](https://nmap.org/download.html). -The easiest way to install RustScan is to use one of the packages provided for your system, such as HomeBrew or Yay for Arch Linux. - -The most universal way is to use `cargo`, Rust's built in package manager (think Pip but for Rust). [Follow this guide to installing Rust & Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html). - -If you face any issues at all, please leave a GitHub issue. I have only tested this on Linux, so there may be issues for Mac OS or Windows. - -Note: sometimes Rust doesn't add Cargo to the path. Please see [this issue](https://github.com/rust-lang/rustup/issues/2436) for how to fix that. - -## 🖥️ Debian / Kali - -Download the .deb file from the releases page: - -[https://github.com/brandonskerritt/RustScan/releases](https://github.com/brandonskerritt/RustScan/releases) - -Run the commpand `dpkg -i` on the file. - -Note: sometimes you can double click the file to achieve the same result. - -## Docker :whale: - -Docker is the recommended way of installing RustScan. This is because: -* It has a high open file descriptor limit, which is one of the [main problems](https://github.com/RustScan/RustScan/issues/40) with RustScan. Now you don't have to fiddle around trying to understand your OS. -* It works on all systems, regardless of OS. Even Windows, which we don't officially support. -* The Docker image uses the latest build from Cargo, our main source-of-truth package. This means that you will always be using the latest version. -* No need to install Rust, Cargo, or Nmap. - -To install Docker, [follow their guide](https://docs.docker.com/engine/install/). - -**Once Docker is installed, run this command against the IP you want to target.** - -``` -docker run -it --rm --name rustscan cmnatic/rustscan:debian-buster rustscan 127.0.0.1 -``` - -Note: this will scan the Docker's localhost, not your own. - -This will download the Docker image. - -Once done, you will no longer need to re-download the image (except when RustScan updates) and can use RustScan like a normal application. - -You will have to run this command every time, so we suggest aliasing it to something memorable. - -``` -alias rustscan='docker run -it --rm --name rustscan cmnatic/rustscan:debian-buster rustscan' -``` - -Then we can: - -``` -rustscan 127.0.0.1 -t 500 -b 1500 -- -A -``` - -## 🍺 HomeBrew - -**Note for Mac users** Mac OS has a very, very small ulimit size. This will negatively impact RustScan by a significant amount. Please use the Docker container, or tell RustScan to up the ulimit size on every run. - -Tap the brew: - -``` -brew tap brandonskerritt/rustscan -``` - -Install it: - -``` -brew install rustscan -``` - -## 🔧 Building it yourself - -1. Git clone the repo. -2. Install Rust. You can do this with `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` which I took from the Rust website https://www.rust-lang.org/tools/install -3. cd into the Git repo, and run `cargo build --release` -4. The binary is located at `target/release/rustscan` -5. Symlink to the binary or something. Whatever you want! +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) +- [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) # 🤸 Usage -``` -rustscan -h -``` +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). -``` -rustscan 1.7.1 -Fast Port Scanner built in Rust. WARNING Do not use this program against sensitive infrastructure since the specified -server may not be able to handle this many socket connections at once. - Discord https://discord.gg/GFrQsGy - GitHub -https://github.com/RustScan/RustScan - -USAGE: - rustscan [FLAGS] [OPTIONS] ... [-- ...] - -FLAGS: - -a, --accessible - -h, --help Prints help information - -q, --quiet Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file - -V, --version Prints version information - -OPTIONS: - -b, --batch-size The batch size for port scanning, it increases or slows the speed of scanning. - Depends on the open file limit of your OS. If you do 65535 it will do every port - at the same time. Although, your OS may not support this [default: 4500] - --scan-order The order of scanning to be performed. The "serial" option will scan ports in - ascending order while the "random" option will scan ports randomly [default: - serial] [possible values: Serial, Random] - -t, --timeout The timeout in milliseconds before a port is assumed to be closed [default: 1500] - -u, --ulimit Automatically ups the ULIMIT with the value you provided - -ARGS: - ... A list of comma separated IP addresses to be scanned - ... The Nmap arguments to run. To use the argument -A, end RustScan's args with '-- -A'. Example: - 'rustscan -T 1500 127.0.0.1 -- -A -sC'. This command adds -Pn -vvv -p $PORTS automatically to - nmap. For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and - vuln)'\"") -``` - -The format is `rustscan -b 500 -T 1500 192.168.0.1` to scan 192.168.0.1 with 500 batch size with a timeout of 1500ms. The timeout is how long RustScan waits for a response until it assumes the port is closed. - -The batch size determines how fast RustScan is. Set it to 65k, and it will scan all 65k ports at the same time. This means at at 65k batch size, RustScan will take TIMEOUT long to scan all ports. Essentially, if timeout is 1000ms, **RustScan can scan in 1 second**. - -Your operating system may not support this, but it is worth it to play around and see where your open file limit is. Shortly I will be releasing a dockerised version with a much larger open file limit, so this will be possible. - -## ⚠️ WARNING - -This program, by default, scans 5000 ports at a time (5000 per second). - -This may cause damage to a server, or may make it incredibly obvious you are scanning the server. - -There are 2 ways to deal with this; -1. Decrease batch size -`rustscan -b 10` will run 10 port scans for 1 second, and then another 10 for 1 second and so on. -2. Increase timeout -`rustscan -T 5000` will mean RustScan waits 5 seconds until it scans the next ports. - -You can also use both of these at the same time, to make it as slow or as fast as you want. A fun favourite is 65535 batch size with 1 second timeout. Theoretically, this scans all 65535 ports in 1 second. - -**Please** do not use this tool against sensitive servers. It is designed mainly for Capture the Flag events, not real world servers with sensitive data. - -## 🚨 Thread Paniced at Main: Too Many Open Files -This is the most common error found in RustScan. - -The open file limit is how many open sockets you can have at any given time. - -This limit changes from OS to OS. - -RustScan does not automatically create defaults (other than 5000) like Nmap does with their -T1, -T2 system. - -By figuring out for yourself the optimal batch size, you will know that RustScan is the most optimised port scanner for your system. - -There are 2 things you can do: -1. Decrease batch size -2. Increase open file limit - -Decreasing batch size slows down the program, so as long as it isn't too drastic, this is a good option. - -Run these 3 commands: - -``` -ulimit -a -ulimit -Hn -ulimit -Sn -``` - -They will give you an idea on the open file limit of your OS. - -If it says "250", run `rustscan -b 240` for a batch size of 240. - -Increasing the open file limit increases speed, but poses danger. Although, **opening more file sockets on the specified IP address may damage it**. - -To open more, set the ulimit to a higher number: - -``` -ulimit -n 5000 -``` - -**Mac OS** -Mac OS has, from what I can tell, a naturally very low open file descriptor limit. The limit for Ubuntu is 8800. The limit for Mac OS is 255! - -In this case, I would say it is safe to increase the open file limit. As most Linux based OS' have limits in the thousands. - -Although, if this breaks anything, please don't blame me. - -**Windows Subsystem for Linux** -Windows Subsystem for Linux does not support ulimit (see issue #39). - -The best way is to use it on a host computer, in Docker, or in a VM that isn't WSL. - -**Automatic Ulimit updating** -We are currently working on automatic Ulimit updating. If it is too high, it will lower itself. If it is too low, it will suggest a higher Ulimit. Watch [this issue](https://github.com/brandonskerritt/RustScan/issues/25) for more. - -## 🔌 Nmap Custom Flags -To run your own nmap commands, end the RustScan command with `-- -A` where `--` indicates "end of RustScan flags, please do not parse anything further" and any flags after that will be entered into nmap. - -RustScan automatically runs `nmap -vvv -p $PORTS $IP`. To make it run `-A`, execute the command `rustscan 127.0.0.1 -- -A`. - -If you want to run commands such as `--script (vuln and safe)`, you will need to enclose it in quotations like so `--script '"(vuln and safe) or default"'`. - -## 🎯 Increasing speed / accuracy -* Batch size - -This increases speed, by allowing us to process more at once. Something experimental I am working on is changing the open file limit. You can do this manually with `ulimit -n 70000` and then running rustscan with `-b 65535`. This _should_ scan all 65535 ports at the exact same time. But this is extremely experimental. - -For non-experimental speed increases, slowly increase the batch size until it no longer gets open ports, or it breaks. - -* Accuracy (and some speed) - -To increase accuracy, the easiest way is to increase the timeout. The default is 1.5 seconds, by setting it to 4 seconds (4000) we are telling RustScan "if we do not hear back from a port in 4 seconds, assume it is closed". - -Decreasing accuracy gives some speed bonus, but my testing found that batch size dramatically changed the speed whereas timeout did, but not so much. +We also have documentation about our config file [here](https://github.com/RustScan/RustScan/wiki/Config-File). # 🎪 Community -Howdy Space Cow-Person 🤠🌌 - -RustScan is always looking for contributors. Whether that's spelling mistakes or major changes, your help is **wanted** and welcomed here. - -Before contributing, read our [code of conduct](https://github.com/RustScan/RustScan/blob/master/CODE_OF_CONDUCT.md). - -TL;DR if you abuse members of our community you will be **perma-banned** 🤗 - -RustScan has 2 major labels for GitHub issues you should look at: -* Good First issue -These are issues for newcomers to open source! -[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) -* Help wanted -These are issues that aren't really for newcomers, but we could still do wiht help! -[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22) - -If you want to, solve the issue or comment on the issue for help. - -The flow for contributing to open source software is: -* Fork the repo -* Make changes -* Pull request to the repo - -And then comment on the issue that you've done. - -RustScan also has some `// TODO`'s in the codebase, which are meant more for the core team but we wouldn't say no to help with these issues. - -If you have any feature suggestions or bugs, leave a GitHub issue. We welcome any and all support :D - -We communicate over Discord. [Click here](https://discord.gg/GFrQsGy) to join our Discord community! - -## Rewarding you -I cannot pay you :-( But, I can place your GitHub profile on the README under `#Contributors` as a thank you! :) - - -Please read the [contributing.md file](contributing.md) +[Read this to learn how to contribute](https://github.com/RustScan/RustScan/wiki/Contributing). ## Contributors ✨ + -[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-26-orange.svg?style=flat-square)](#contributors-) Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -373,7 +159,28 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Atul Bhosale

💻 +
Thomas Gotwig

📦
Rémi Gourdon

📖 💻 +
Ben (CMNatic)

💻 📖 🎨 +
Alessandro Ferrari

🖋 +
Phenomite

🖋 +
Sandro

🖋 🐛 💻 + + +
Cass

📦 💻 🐛 +
Niklas Mohrin

📖 💻 🐛 +
Artem Polishchuk

📦 +
buermarc

💻 +
bergabman

💻 🐛 🎨 +
Dmitry Savintsev

💻 +
Sebastian Andersson

💻 + + +
Matt Corbin

💻 +
RootSploit

📝 +
eiffel-fl

💻 +
Y.Horie

💻 +
Oskar

💻 ⚠️ diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..2776c3b --- /dev/null +++ b/config.toml @@ -0,0 +1,1004 @@ +ip="127.0.0.1" + +# The hashmap of {ports: {1:1}} +[ports] +1 = 1 +3 = 1 +4 = 1 +6 = 1 +7 = 1 +9 = 1 +13 = 1 +17 = 1 +19 = 1 +20 = 1 +21 = 1 +22 = 1 +23 = 1 +24 = 1 +25 = 1 +26 = 1 +30 = 1 +32 = 1 +33 = 1 +37 = 1 +42 = 1 +43 = 1 +49 = 1 +53 = 1 +70 = 1 +79 = 1 +80 = 1 +81 = 1 +82 = 1 +83 = 1 +84 = 1 +85 = 1 +88 = 1 +89 = 1 +90 = 1 +99 = 1 +100 = 1 +106 = 1 +109 = 1 +110 = 1 +111 = 1 +113 = 1 +119 = 1 +125 = 1 +135 = 1 +139 = 1 +143 = 1 +144 = 1 +146 = 1 +161 = 1 +163 = 1 +179 = 1 +199 = 1 +211 = 1 +212 = 1 +222 = 1 +254 = 1 +255 = 1 +256 = 1 +259 = 1 +264 = 1 +280 = 1 +301 = 1 +306 = 1 +311 = 1 +340 = 1 +366 = 1 +389 = 1 +406 = 1 +407 = 1 +416 = 1 +417 = 1 +425 = 1 +427 = 1 +443 = 1 +444 = 1 +445 = 1 +458 = 1 +464 = 1 +465 = 1 +481 = 1 +497 = 1 +500 = 1 +512 = 1 +513 = 1 +514 = 1 +515 = 1 +524 = 1 +541 = 1 +543 = 1 +544 = 1 +545 = 1 +548 = 1 +554 = 1 +555 = 1 +563 = 1 +587 = 1 +593 = 1 +616 = 1 +617 = 1 +625 = 1 +631 = 1 +636 = 1 +646 = 1 +648 = 1 +666 = 1 +667 = 1 +668 = 1 +683 = 1 +687 = 1 +691 = 1 +700 = 1 +705 = 1 +711 = 1 +714 = 1 +720 = 1 +722 = 1 +726 = 1 +749 = 1 +765 = 1 +777 = 1 +783 = 1 +787 = 1 +800 = 1 +801 = 1 +808 = 1 +843 = 1 +873 = 1 +880 = 1 +888 = 1 +898 = 1 +900 = 1 +901 = 1 +902 = 1 +903 = 1 +911 = 1 +912 = 1 +981 = 1 +987 = 1 +990 = 1 +992 = 1 +993 = 1 +995 = 1 +999 = 1 +1000 = 1 +1001 = 1 +1002 = 1 +1007 = 1 +1009 = 1 +1010 = 1 +1011 = 1 +1021 = 1 +1022 = 1 +1023 = 1 +1024 = 1 +1025 = 1 +1026 = 1 +1027 = 1 +1028 = 1 +1029 = 1 +1030 = 1 +1031 = 1 +1032 = 1 +1033 = 1 +1034 = 1 +1035 = 1 +1036 = 1 +1037 = 1 +1038 = 1 +1039 = 1 +1040 = 1 +1041 = 1 +1042 = 1 +1043 = 1 +1044 = 1 +1045 = 1 +1046 = 1 +1047 = 1 +1048 = 1 +1049 = 1 +1050 = 1 +1051 = 1 +1052 = 1 +1053 = 1 +1054 = 1 +1055 = 1 +1056 = 1 +1057 = 1 +1058 = 1 +1059 = 1 +1060 = 1 +1061 = 1 +1062 = 1 +1063 = 1 +1064 = 1 +1065 = 1 +1066 = 1 +1067 = 1 +1068 = 1 +1069 = 1 +1070 = 1 +1071 = 1 +1072 = 1 +1073 = 1 +1074 = 1 +1075 = 1 +1076 = 1 +1077 = 1 +1078 = 1 +1079 = 1 +1080 = 1 +1081 = 1 +1082 = 1 +1083 = 1 +1084 = 1 +1085 = 1 +1086 = 1 +1087 = 1 +1088 = 1 +1089 = 1 +1090 = 1 +1091 = 1 +1092 = 1 +1093 = 1 +1094 = 1 +1095 = 1 +1096 = 1 +1097 = 1 +1098 = 1 +1099 = 1 +1100 = 1 +1102 = 1 +1104 = 1 +1105 = 1 +1106 = 1 +1107 = 1 +1108 = 1 +1110 = 1 +1111 = 1 +1112 = 1 +1113 = 1 +1114 = 1 +1117 = 1 +1119 = 1 +1121 = 1 +1122 = 1 +1123 = 1 +1124 = 1 +1126 = 1 +1130 = 1 +1131 = 1 +1132 = 1 +1137 = 1 +1138 = 1 +1141 = 1 +1145 = 1 +1147 = 1 +1148 = 1 +1149 = 1 +1151 = 1 +1152 = 1 +1154 = 1 +1163 = 1 +1164 = 1 +1165 = 1 +1166 = 1 +1169 = 1 +1174 = 1 +1175 = 1 +1183 = 1 +1185 = 1 +1186 = 1 +1187 = 1 +1192 = 1 +1198 = 1 +1199 = 1 +1201 = 1 +1213 = 1 +1216 = 1 +1217 = 1 +1218 = 1 +1233 = 1 +1234 = 1 +1236 = 1 +1244 = 1 +1247 = 1 +1248 = 1 +1259 = 1 +1271 = 1 +1272 = 1 +1277 = 1 +1287 = 1 +1296 = 1 +1300 = 1 +1301 = 1 +1309 = 1 +1310 = 1 +1311 = 1 +1322 = 1 +1328 = 1 +1334 = 1 +1352 = 1 +1417 = 1 +1433 = 1 +1434 = 1 +1443 = 1 +1455 = 1 +1461 = 1 +1494 = 1 +1500 = 1 +1501 = 1 +1503 = 1 +1521 = 1 +1524 = 1 +1533 = 1 +1556 = 1 +1580 = 1 +1583 = 1 +1594 = 1 +1600 = 1 +1641 = 1 +1658 = 1 +1666 = 1 +1687 = 1 +1688 = 1 +1700 = 1 +1717 = 1 +1718 = 1 +1719 = 1 +1720 = 1 +1721 = 1 +1723 = 1 +1755 = 1 +1761 = 1 +1782 = 1 +1783 = 1 +1801 = 1 +1805 = 1 +1812 = 1 +1839 = 1 +1840 = 1 +1862 = 1 +1863 = 1 +1864 = 1 +1875 = 1 +1900 = 1 +1914 = 1 +1935 = 1 +1947 = 1 +1971 = 1 +1972 = 1 +1974 = 1 +1984 = 1 +1998 = 1 +1999 = 1 +2000 = 1 +2001 = 1 +2002 = 1 +2003 = 1 +2004 = 1 +2005 = 1 +2006 = 1 +2007 = 1 +2008 = 1 +2009 = 1 +2010 = 1 +2013 = 1 +2020 = 1 +2021 = 1 +2022 = 1 +2030 = 1 +2033 = 1 +2034 = 1 +2035 = 1 +2038 = 1 +2040 = 1 +2041 = 1 +2042 = 1 +2043 = 1 +2045 = 1 +2046 = 1 +2047 = 1 +2048 = 1 +2049 = 1 +2065 = 1 +2068 = 1 +2099 = 1 +2100 = 1 +2103 = 1 +2105 = 1 +2106 = 1 +2107 = 1 +2111 = 1 +2119 = 1 +2121 = 1 +2126 = 1 +2135 = 1 +2144 = 1 +2160 = 1 +2161 = 1 +2170 = 1 +2179 = 1 +2190 = 1 +2191 = 1 +2196 = 1 +2200 = 1 +2222 = 1 +2251 = 1 +2260 = 1 +2288 = 1 +2301 = 1 +2323 = 1 +2366 = 1 +2381 = 1 +2382 = 1 +2383 = 1 +2393 = 1 +2394 = 1 +2399 = 1 +2401 = 1 +2492 = 1 +2500 = 1 +2522 = 1 +2525 = 1 +2557 = 1 +2601 = 1 +2602 = 1 +2604 = 1 +2605 = 1 +2607 = 1 +2608 = 1 +2638 = 1 +2701 = 1 +2702 = 1 +2710 = 1 +2717 = 1 +2718 = 1 +2725 = 1 +2800 = 1 +2809 = 1 +2811 = 1 +2869 = 1 +2875 = 1 +2909 = 1 +2910 = 1 +2920 = 1 +2967 = 1 +2968 = 1 +2998 = 1 +3000 = 1 +3001 = 1 +3003 = 1 +3005 = 1 +3006 = 1 +3007 = 1 +3011 = 1 +3013 = 1 +3017 = 1 +3030 = 1 +3031 = 1 +3052 = 1 +3071 = 1 +3077 = 1 +3128 = 1 +3168 = 1 +3211 = 1 +3221 = 1 +3260 = 1 +3261 = 1 +3268 = 1 +3269 = 1 +3283 = 1 +3300 = 1 +3301 = 1 +3306 = 1 +3322 = 1 +3323 = 1 +3324 = 1 +3325 = 1 +3333 = 1 +3351 = 1 +3367 = 1 +3369 = 1 +3370 = 1 +3371 = 1 +3372 = 1 +3389 = 1 +3390 = 1 +3404 = 1 +3476 = 1 +3493 = 1 +3517 = 1 +3527 = 1 +3546 = 1 +3551 = 1 +3580 = 1 +3659 = 1 +3689 = 1 +3690 = 1 +3703 = 1 +3737 = 1 +3766 = 1 +3784 = 1 +3800 = 1 +3801 = 1 +3809 = 1 +3814 = 1 +3826 = 1 +3827 = 1 +3828 = 1 +3851 = 1 +3869 = 1 +3871 = 1 +3878 = 1 +3880 = 1 +3889 = 1 +3905 = 1 +3914 = 1 +3918 = 1 +3920 = 1 +3945 = 1 +3971 = 1 +3986 = 1 +3995 = 1 +3998 = 1 +4000 = 1 +4001 = 1 +4002 = 1 +4003 = 1 +4004 = 1 +4005 = 1 +4006 = 1 +4045 = 1 +4111 = 1 +4125 = 1 +4126 = 1 +4129 = 1 +4224 = 1 +4242 = 1 +4279 = 1 +4321 = 1 +4343 = 1 +4443 = 1 +4444 = 1 +4445 = 1 +4446 = 1 +4449 = 1 +4550 = 1 +4567 = 1 +4662 = 1 +4848 = 1 +4899 = 1 +4900 = 1 +4998 = 1 +5000 = 1 +5001 = 1 +5002 = 1 +5003 = 1 +5004 = 1 +5009 = 1 +5030 = 1 +5033 = 1 +5050 = 1 +5051 = 1 +5054 = 1 +5060 = 1 +5061 = 1 +5080 = 1 +5087 = 1 +5100 = 1 +5101 = 1 +5102 = 1 +5120 = 1 +5190 = 1 +5200 = 1 +5214 = 1 +5221 = 1 +5222 = 1 +5225 = 1 +5226 = 1 +5269 = 1 +5280 = 1 +5298 = 1 +5357 = 1 +5405 = 1 +5414 = 1 +5431 = 1 +5432 = 1 +5440 = 1 +5500 = 1 +5510 = 1 +5544 = 1 +5550 = 1 +5555 = 1 +5560 = 1 +5566 = 1 +5631 = 1 +5633 = 1 +5666 = 1 +5678 = 1 +5679 = 1 +5718 = 1 +5730 = 1 +5800 = 1 +5801 = 1 +5802 = 1 +5810 = 1 +5811 = 1 +5815 = 1 +5822 = 1 +5825 = 1 +5850 = 1 +5859 = 1 +5862 = 1 +5877 = 1 +5900 = 1 +5901 = 1 +5902 = 1 +5903 = 1 +5904 = 1 +5906 = 1 +5907 = 1 +5910 = 1 +5911 = 1 +5915 = 1 +5922 = 1 +5925 = 1 +5950 = 1 +5952 = 1 +5959 = 1 +5960 = 1 +5961 = 1 +5962 = 1 +5963 = 1 +5987 = 1 +5988 = 1 +5989 = 1 +5998 = 1 +5999 = 1 +6000 = 1 +6001 = 1 +6002 = 1 +6003 = 1 +6004 = 1 +6005 = 1 +6006 = 1 +6007 = 1 +6009 = 1 +6025 = 1 +6059 = 1 +6100 = 1 +6101 = 1 +6106 = 1 +6112 = 1 +6123 = 1 +6129 = 1 +6156 = 1 +6346 = 1 +6389 = 1 +6502 = 1 +6510 = 1 +6543 = 1 +6547 = 1 +6565 = 1 +6566 = 1 +6567 = 1 +6580 = 1 +6646 = 1 +6666 = 1 +6667 = 1 +6668 = 1 +6669 = 1 +6689 = 1 +6692 = 1 +6699 = 1 +6779 = 1 +6788 = 1 +6789 = 1 +6792 = 1 +6839 = 1 +6881 = 1 +6901 = 1 +6969 = 1 +7000 = 1 +7001 = 1 +7002 = 1 +7004 = 1 +7007 = 1 +7019 = 1 +7025 = 1 +7070 = 1 +7100 = 1 +7103 = 1 +7106 = 1 +7200 = 1 +7201 = 1 +7402 = 1 +7435 = 1 +7443 = 1 +7496 = 1 +7512 = 1 +7625 = 1 +7627 = 1 +7676 = 1 +7741 = 1 +7777 = 1 +7778 = 1 +7800 = 1 +7911 = 1 +7920 = 1 +7921 = 1 +7937 = 1 +7938 = 1 +7999 = 1 +8000 = 1 +8001 = 1 +8002 = 1 +8007 = 1 +8008 = 1 +8009 = 1 +8010 = 1 +8011 = 1 +8021 = 1 +8022 = 1 +8031 = 1 +8042 = 1 +8045 = 1 +8080 = 1 +8081 = 1 +8082 = 1 +8083 = 1 +8084 = 1 +8085 = 1 +8086 = 1 +8087 = 1 +8088 = 1 +8089 = 1 +8090 = 1 +8093 = 1 +8099 = 1 +8100 = 1 +8180 = 1 +8181 = 1 +8192 = 1 +8193 = 1 +8194 = 1 +8200 = 1 +8222 = 1 +8254 = 1 +8290 = 1 +8291 = 1 +8292 = 1 +8300 = 1 +8333 = 1 +8383 = 1 +8400 = 1 +8402 = 1 +8443 = 1 +8500 = 1 +8600 = 1 +8649 = 1 +8651 = 1 +8652 = 1 +8654 = 1 +8701 = 1 +8800 = 1 +8873 = 1 +8888 = 1 +8899 = 1 +8994 = 1 +9000 = 1 +9001 = 1 +9002 = 1 +9003 = 1 +9009 = 1 +9010 = 1 +9011 = 1 +9040 = 1 +9050 = 1 +9071 = 1 +9080 = 1 +9081 = 1 +9090 = 1 +9091 = 1 +9099 = 1 +9100 = 1 +9101 = 1 +9102 = 1 +9103 = 1 +9110 = 1 +9111 = 1 +9200 = 1 +9207 = 1 +9220 = 1 +9290 = 1 +9415 = 1 +9418 = 1 +9485 = 1 +9500 = 1 +9502 = 1 +9503 = 1 +9535 = 1 +9575 = 1 +9593 = 1 +9594 = 1 +9595 = 1 +9618 = 1 +9666 = 1 +9876 = 1 +9877 = 1 +9878 = 1 +9898 = 1 +9900 = 1 +9917 = 1 +9929 = 1 +9943 = 1 +9944 = 1 +9968 = 1 +9998 = 1 +9999 = 1 +10000 = 1 +10001 = 1 +10002 = 1 +10003 = 1 +10004 = 1 +10009 = 1 +10010 = 1 +10012 = 1 +10024 = 1 +10025 = 1 +10082 = 1 +10180 = 1 +10215 = 1 +10243 = 1 +10566 = 1 +10616 = 1 +10617 = 1 +10621 = 1 +10626 = 1 +10628 = 1 +10629 = 1 +10778 = 1 +11110 = 1 +11111 = 1 +11967 = 1 +12000 = 1 +12174 = 1 +12265 = 1 +12345 = 1 +13456 = 1 +13722 = 1 +13782 = 1 +13783 = 1 +14000 = 1 +14238 = 1 +14441 = 1 +14442 = 1 +15000 = 1 +15002 = 1 +15003 = 1 +15004 = 1 +15660 = 1 +15742 = 1 +16000 = 1 +16001 = 1 +16012 = 1 +16016 = 1 +16018 = 1 +16080 = 1 +16113 = 1 +16992 = 1 +16993 = 1 +17877 = 1 +17988 = 1 +18040 = 1 +18101 = 1 +18988 = 1 +19101 = 1 +19283 = 1 +19315 = 1 +19350 = 1 +19780 = 1 +19801 = 1 +19842 = 1 +20000 = 1 +20005 = 1 +20031 = 1 +20221 = 1 +20222 = 1 +20828 = 1 +21571 = 1 +22939 = 1 +23502 = 1 +24444 = 1 +24800 = 1 +25734 = 1 +25735 = 1 +26214 = 1 +27000 = 1 +27352 = 1 +27353 = 1 +27355 = 1 +27356 = 1 +27715 = 1 +28201 = 1 +30000 = 1 +30718 = 1 +30951 = 1 +31038 = 1 +31337 = 1 +32768 = 1 +32769 = 1 +32770 = 1 +32771 = 1 +32772 = 1 +32773 = 1 +32774 = 1 +32775 = 1 +32776 = 1 +32777 = 1 +32778 = 1 +32779 = 1 +32780 = 1 +32781 = 1 +32782 = 1 +32783 = 1 +32784 = 1 +32785 = 1 +33354 = 1 +33899 = 1 +34571 = 1 +34572 = 1 +34573 = 1 +35500 = 1 +38292 = 1 +40193 = 1 +40911 = 1 +41511 = 1 +42510 = 1 +44176 = 1 +44442 = 1 +44443 = 1 +44501 = 1 +45100 = 1 +48080 = 1 +49152 = 1 +49153 = 1 +49154 = 1 +49155 = 1 +49156 = 1 +49157 = 1 +49158 = 1 +49159 = 1 +49160 = 1 +49161 = 1 +49163 = 1 +49165 = 1 +49167 = 1 +49175 = 1 +49176 = 1 +49400 = 1 +49999 = 1 +50000 = 1 +50001 = 1 +50002 = 1 +50003 = 1 +50006 = 1 +50300 = 1 +50389 = 1 +50500 = 1 +50636 = 1 +50800 = 1 +51103 = 1 +51493 = 1 +52673 = 1 +52822 = 1 +52848 = 1 +52869 = 1 +54045 = 1 +54328 = 1 +55055 = 1 +55056 = 1 +55555 = 1 +55600 = 1 +56737 = 1 +56738 = 1 +57294 = 1 +57797 = 1 +58080 = 1 +60020 = 1 +60443 = 1 +61532 = 1 +61900 = 1 +62078 = 1 +63331 = 1 +64623 = 1 +64680 = 1 +65000 = 1 +65129 = 1 +65389 = 1 diff --git a/contributing.Dockerfile b/contributing.Dockerfile new file mode 100644 index 0000000..da6470c --- /dev/null +++ b/contributing.Dockerfile @@ -0,0 +1,5 @@ +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 \ No newline at end of file diff --git a/contributing.md b/contributing.md index a28fe64..aa6eae9 100644 --- a/contributing.md +++ b/contributing.md @@ -7,19 +7,21 @@ Before contributing, read our [code of conduct](https://github.com/RustScan/Rust TL;DR if you abuse members of our community you will be **perma-banned** with no chance to get unbanned. No warnings either. 🤗 RustScan has 2 major labels for GitHub issues you should look at: -* Good First issue -These are issues for newcomers to open source! -[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) -* Help wanted -These are issues that aren't really for newcomers, but we could still do wiht help! -[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22) + +- Good First issue + These are issues for newcomers to open source! + [https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) +- Help wanted + These are issues that aren't really for newcomers, but we could still do with help! + [https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22) If you want to, solve the issue or comment on the issue for help. The flow for contributing to open source software is: -* Fork the repo -* Make changes -* Pull request to the repo + +- Fork the repo +- Make changes +- Pull request to the repo And then comment on the issue that you've done. @@ -28,4 +30,40 @@ RustScan also has some `// TODO`'s in the codebase, which are meant more for the If you have any feature suggestions or bugs, leave a GitHub issue. We welcome any and all support :D ## Rewarding you + I cannot pay you :-( But, I can place your GitHub profile on the README under `#Contributors` as a thank you! :) + +## Contributing development environment + +To ease contribution to RustScan, you can use the `contributing.Dockerfile` to create a Docker image ready to build and play with RustScan. +To build it you just need to run: + +```bash +you@home:~/RustScan$ docker build -t rustscan_contributing -f contributing.Dockerfile +``` + +Then you need to run the container with a volume so it can access, _with read and write permissions_, to RustScan files: + +```bash +you@home:~/RustScan$ docker run -ti --rm -v "$PWD":/rustscan -w /rustscan rustscan_contributing bash +``` + +You can now modify RustScan files with your favorite editor, once you want to compile and test your modifications, type the following in the container prompt: + +```bash +root@container:/rustscan# cargo build +``` + +You are now ready to use RustScan: + +```bash +root@container:/rustscan# cargo run -- -b 2000 -t 5000 -a 127.0.0.1 +``` + +You can also format, lint with `clippy` and test the code with the following commands: + +```bash +root@container:/rustscan# cargo fmt +root@container:/rustscan# cargo clippy +root@container:/rustscan# cargo test +``` diff --git a/fixtures/.rustscan_scripts/test_script.pl b/fixtures/.rustscan_scripts/test_script.pl new file mode 100644 index 0000000..c51cc01 --- /dev/null +++ b/fixtures/.rustscan_scripts/test_script.pl @@ -0,0 +1,23 @@ +#!/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++; +} \ No newline at end of file diff --git a/fixtures/.rustscan_scripts/test_script.py b/fixtures/.rustscan_scripts/test_script.py new file mode 100644 index 0000000..b990fd8 --- /dev/null +++ b/fixtures/.rustscan_scripts/test_script.py @@ -0,0 +1,13 @@ +#!/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)) \ No newline at end of file diff --git a/fixtures/.rustscan_scripts/test_script.sh b/fixtures/.rustscan_scripts/test_script.sh new file mode 100644 index 0000000..20b2f0e --- /dev/null +++ b/fixtures/.rustscan_scripts/test_script.sh @@ -0,0 +1,12 @@ +#!/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 $@ \ No newline at end of file diff --git a/fixtures/.rustscan_scripts/test_script.txt b/fixtures/.rustscan_scripts/test_script.txt new file mode 100644 index 0000000..fbb58c4 --- /dev/null +++ b/fixtures/.rustscan_scripts/test_script.txt @@ -0,0 +1,9 @@ +#!intentional_blank_line +#tags = ["core_approved", "example"] +#developer = [ "example", "https://example.org" ] +#ports_separator = "," +#call_format = "nmap -vvv -p {{port}} {{ip}}" + +# 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" \ No newline at end of file diff --git a/fixtures/.rustscan_scripts/test_script_invalid_headers.txt b/fixtures/.rustscan_scripts/test_script_invalid_headers.txt new file mode 100644 index 0000000..455cc67 --- /dev/null +++ b/fixtures/.rustscan_scripts/test_script_invalid_headers.txt @@ -0,0 +1,7 @@ +#!intentional_blank_line +#tags = "core_approved" +#developer = [ "example", "https://example.org" ] +#ports_separator = "," +#call_format = "nmap -vvv -p {{port}} {{ip}}" + +# tags has to be an array, thus this file won't be parsed \ No newline at end of file diff --git a/fixtures/empty_hosts.txt b/fixtures/empty_hosts.txt new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/hosts.txt b/fixtures/hosts.txt new file mode 100644 index 0000000..ef3dcb8 --- /dev/null +++ b/fixtures/hosts.txt @@ -0,0 +1,6 @@ +127.0.0.1 +google.com +example.com +66666666666666.666666666666666666.66666666666666666.6666666666 +.... +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.radiatorFixtures \ No newline at end of file diff --git a/fixtures/naughty_strings.txt b/fixtures/naughty_strings.txt new file mode 100644 index 0000000..9e3e816 --- /dev/null +++ b/fixtures/naughty_strings.txt @@ -0,0 +1,42 @@ +# https://github.com/minimaxir/big-list-of-naughty-strings + + +­؀؁؂؃؄؅؜۝܏᠎​‌‍‎‏‪‫‬‭‮⁠⁡⁢⁣⁤⁦⁧⁨⁩𑂽𛲠𛲡𛲢𛲣𝅳𝅴𝅵𝅶𝅷𝅸𝅹𝅺󠀁󠀠󠀡󠀢󠀣󠀤󠀥󠀦󠀧󠀨󠀩󠀪󠀫󠀬󠀭󠀮󠀯󠀰󠀱󠀲󠀳󠀴󠀵󠀶󠀷󠀸󠀹󠀺󠀻󠀼󠀽󠀾󠀿󠁀󠁁󠁂󠁃󠁄󠁅󠁆󠁇󠁈󠁉󠁊󠁋󠁌󠁍󠁎󠁏󠁐󠁑󠁒󠁓󠁔󠁕󠁖󠁗󠁘󠁙󠁚󠁛󠁜󠁝󠁞󠁟󠁠󠁡󠁢󠁣󠁤󠁥󠁦󠁧󠁨󠁩󠁪󠁫󠁬󠁭󠁮󠁯󠁰󠁱󠁲󠁳󠁴󠁵󠁶󠁷󠁸󠁹󠁺󠁻󠁼󠁽󠁾󠁿 + +Ω≈ç√∫˜µ≤≥÷ +åß∂ƒ©˙∆˚¬…æ +œ∑´®†¥¨ˆøπ“‘ +¡™£¢∞§¶•ªº–≠ +¸˛Ç◊ı˜Â¯˘¿ +ÅÍÎÏ˝ÓÔÒÚÆ☃ +Œ„´‰ˇÁ¨ˆØ∏”’ +`⁄€‹›fifl‡°·‚—± +⅛⅜⅝⅞ +ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя +٠١٢٣٤٥٦٧٨٩ + + + +⁰⁴⁵ +₀₁₂ +⁰⁴⁵₀₁₂ +ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ + +田中さんにあげて下さい +パーティーへ行かないか +和製漢語 +部落格 +사회과학원 어학연구소 +찦차를 타고 온 펲시맨과 쑛다리 똠방각하 +社會科學院語學研究所 +울란바토르 +𠜎𠜱𠝹𠱓𠱸𠲖𠳏 +𝐓𝐡𝐞 𝐪𝐮𝐢𝐜𝐤 𝐛𝐫𝐨𝐰𝐧 𝐟𝐨𝐱 𝐣𝐮𝐦𝐩𝐬 𝐨𝐯𝐞𝐫 𝐭𝐡𝐞 𝐥𝐚𝐳𝐲 𝐝𝐨𝐠 +𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌 +𝑻𝒉𝒆 𝒒𝒖𝒊𝒄𝒌 𝒃𝒓𝒐𝒘𝒏 𝒇𝒐𝒙 𝒋𝒖𝒎𝒑𝒔 𝒐𝒗𝒆𝒓 𝒕𝒉𝒆 𝒍𝒂𝒛𝒚 𝒅𝒐𝒈 +𝓣𝓱𝓮 𝓺𝓾𝓲𝓬𝓴 𝓫𝓻𝓸𝔀𝓷 𝓯𝓸𝔁 𝓳𝓾𝓶𝓹𝓼 𝓸𝓿𝓮𝓻 𝓽𝓱𝓮 𝓵𝓪𝔃𝔂 𝓭𝓸𝓰 +𝕋𝕙𝕖 𝕢𝕦𝕚𝕔𝕜 𝕓𝕣𝕠𝕨𝕟 𝕗𝕠𝕩 𝕛𝕦𝕞𝕡𝕤 𝕠𝕧𝕖𝕣 𝕥𝕙𝕖 𝕝𝕒𝕫𝕪 𝕕𝕠𝕘 +𝚃𝚑𝚎 𝚚𝚞𝚒𝚌𝚔 𝚋𝚛𝚘𝚠𝚗 𝚏𝚘𝚡 𝚓𝚞𝚖𝚙𝚜 𝚘𝚟𝚎𝚛 𝚝𝚑𝚎 𝚕𝚊𝚣𝚢 𝚍𝚘𝚐 +⒯⒣⒠ ⒬⒰⒤⒞⒦ ⒝⒭⒪⒲⒩ ⒡⒪⒳ ⒥⒰⒨⒫⒮ ⒪⒱⒠⒭ ⒯⒣⒠ ⒧⒜⒵⒴ ⒟⒪⒢ + +ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو. \ No newline at end of file diff --git a/fixtures/test_rustscan_scripts.toml b/fixtures/test_rustscan_scripts.toml new file mode 100644 index 0000000..f9aa918 --- /dev/null +++ b/fixtures/test_rustscan_scripts.toml @@ -0,0 +1,14 @@ +# Test/Example ScriptConfig file + +# Tags to filter on scripts. Only scripts containing all these tags will run. +tags = ["core_approved", "example"] + +# If it's present then only those scripts will run which has a tag ports = "80". Not yet implemented. +# +# ex.: +# ports = ["80"] +# ports = ["80","81","8080"] +ports = ["80"] + +# Only this developer(s) scripts to run. Not yet implemented. +developer = ["example"] \ No newline at end of file diff --git a/pictures/accessible.gif b/pictures/accessible.gif new file mode 100644 index 0000000..10d78ca Binary files /dev/null and b/pictures/accessible.gif differ diff --git a/pictures/accessible.yml b/pictures/accessible.yml new file mode 100644 index 0000000..9b430ff --- /dev/null +++ b/pictures/accessible.yml @@ -0,0 +1,131 @@ +# The configurations that used for the recording, feel free to edit them +config: + + # Specify a command to be executed + # like `/bin/bash -l`, `ls`, or any other commands + # the default is bash for Linux + # or powershell.exe for Windows + command: zsh + + # Specify the current working directory path + # the default is the current working directory path + cwd: /home/bee/Documents/RustScan + + # Export additional ENV variables + env: + recording: true + + # Explicitly set the number of columns + # or use `auto` to take the current + # number of columns of your shell + cols: 107 + + # Explicitly set the number of rows + # or use `auto` to take the current + # number of rows of your shell + rows: 43 + + # Amount of times to repeat GIF + # If value is -1, play once + # If value is 0, loop indefinitely + # If value is a positive number, loop n times + repeat: 0 + + # Quality + # 1 - 100 + quality: 100 + + # Delay between frames in ms + # If the value is `auto` use the actual recording delays + frameDelay: auto + + # Maximum delay between frames in ms + # Ignored if the `frameDelay` isn't set to `auto` + # Set to `auto` to prevent limiting the max idle time + maxIdleTime: 2000 + + # The surrounding frame box + # The `type` can be null, window, floating, or solid` + # To hide the title use the value null + # Don't forget to add a backgroundColor style with a null as type + frameBox: + type: floating + title: Terminalizer + style: + border: 0px black solid + # boxShadow: none + # margin: 0px + + # Add a watermark image to the rendered gif + # You need to specify an absolute path for + # the image on your machine or a URL, and you can also + # add your own CSS styles + watermark: + imagePath: null + style: + position: absolute + right: 15px + bottom: 15px + width: 100px + opacity: 0.9 + + # Cursor style can be one of + # `block`, `underline`, or `bar` + cursorStyle: block + + # Font family + # You can use any font that is installed on your machine + # in CSS-like syntax + fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace" + + # The size of the font + fontSize: 12 + + # The height of lines + lineHeight: 1 + + # The spacing between letters + letterSpacing: 0 + + # Theme + theme: + background: "transparent" + foreground: "#afafaf" + cursor: "#c7c7c7" + black: "#232628" + red: "#fc4384" + green: "#b3e33b" + yellow: "#ffa727" + blue: "#75dff2" + magenta: "#ae89fe" + cyan: "#708387" + white: "#d5d5d0" + brightBlack: "#626566" + brightRed: "#ff7fac" + brightGreen: "#c8ed71" + brightYellow: "#ebdf86" + brightBlue: "#75dff2" + brightMagenta: "#ae89fe" + brightCyan: "#b1c6ca" + brightWhite: "#f9f9f4" + +# Records, feel free to edit them +records: + - delay: 436 + content: "\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive:~/Documents/RustScan\a\e]1;..ents/RustScan\a" + - delay: 162 + content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m master\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.10.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.46.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h" + - delay: 471 + content: "r\brustscan 127.0.0.1 --accessible --no-nmap --ulimit 5000\e[55D\e[7mr\e[7mu\e[7ms\e[7mt\e[7ms\e[7mc\e[7ma\e[7mn\e[7m \e[7m1\e[7m2\e[7m7\e[7m.\e[7m0\e[7m.\e[7m0\e[7m.\e[7m1\e[7m \e[7m-\e[7m-\e[7ma\e[7mc\e[7mc\e[7me\e[7ms\e[7ms\e[7mi\e[7mb\e[7ml\e[7me\e[7m \e[7m-\e[7m-\e[7mn\e[7mo\e[7m-\e[7mn\e[7mm\e[7ma\e[7mp\e[7m \e[7m-\e[7m-\e[7mu\e[7ml\e[7mi\e[7mm\e[7mi\e[7mt\e[7m \e[7m5\e[7m0\e[7m0\e[7m0\e[27m" + - delay: 269 + content: "\e[55D\e[27mr\e[27mu\e[27ms\e[27mt\e[27ms\e[27mc\e[27ma\e[27mn\e[27m \e[27m1\e[27m2\e[27m7\e[27m.\e[27m0\e[27m.\e[27m0\e[27m.\e[27m1\e[27m \e[27m-\e[27m-\e[27ma\e[27mc\e[27mc\e[27me\e[27ms\e[27ms\e[27mi\e[27mb\e[27ml\e[27me\e[27m \e[27m-\e[27m-\e[27mn\e[27mo\e[27m-\e[27mn\e[27mm\e[27ma\e[27mp\e[27m \e[27m-\e[27m-\e[27mu\e[27ml\e[27mi\e[27mm\e[27mi\e[27mt\e[27m \e[27m5\e[27m0\e[27m0\e[27m0\e[?1l\e>\e[?2004l\r\r\n\e]2;rustscan 127.0.0.1 --accessible --no-nmap --ulimit 5000\a\e]1;rustscan\aAutomatically increasing ulimit value to 5000.\r\n" + - delay: 1536 + content: "Open 127.0.0.1:38602\r\n" + - delay: 395 + content: "Open 127.0.0.1:50078\r\n" + - delay: 450 + content: "127.0.0.1 -> [38602,50078]\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive:~/Documents/RustScan\a\e]1;..ents/RustScan\a" + - delay: 143 + content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m master\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.10.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.46.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mtook \e[0m\e[1m\e[33m2s\e[0m\e[33m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h" + - delay: 563 + content: "\e[?2004l\r\r\n" diff --git a/pictures/adaptive.gif b/pictures/adaptive.gif new file mode 100644 index 0000000..f983b84 Binary files /dev/null and b/pictures/adaptive.gif differ diff --git a/pictures/apple.png b/pictures/apple.png index 50ba5b6..cf58fec 100644 Binary files a/pictures/apple.png and b/pictures/apple.png differ diff --git a/pictures/fast.gif b/pictures/fast.gif new file mode 100644 index 0000000..cdd896f Binary files /dev/null and b/pictures/fast.gif differ diff --git a/pictures/rustscan.png b/pictures/rustscan.png index e3f01b4..cc562db 100644 Binary files a/pictures/rustscan.png and b/pictures/rustscan.png differ diff --git a/pictures/scripts.gif b/pictures/scripts.gif new file mode 100644 index 0000000..5f643ee Binary files /dev/null and b/pictures/scripts.gif differ diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs new file mode 100644 index 0000000..f378411 --- /dev/null +++ b/src/benchmark/mod.rs @@ -0,0 +1,89 @@ +//! # Usage +//! ``` +//! // Initiate Benchmark vector +//! let mut bm = Benchmark::init(); +//! // Start named timer with name +//! let mut example_bench = NamedTimer::start("Example Bench"); +//! // Stop named timer +//! example_bench.stop(); +//! // Add named timer to Benchmarks +//! bm.push(example_bench); +//! // Print Benchmark Summary +//! info!({}, bm.summary()); +//! ``` +use std::time::Instant; + +/// A Benchmark struct to hold NamedTimers with name, start and end Instants, +#[derive(Debug)] +pub struct Benchmark { + named_timers: Vec, +} + +impl Benchmark { + pub fn init() -> Self { + Self { + named_timers: Vec::new(), + } + } + pub fn push(&mut self, timer: NamedTimer) { + self.named_timers.push(timer); + } + + /// Summary of the benchmarks will destruct the vector, + /// formats every element the same way and return + /// a single String with all the available information + /// for easy printing + pub fn summary(&self) -> String { + let mut summary = String::from("\nRustScan Benchmark Summary"); + + for timer in &self.named_timers { + if timer.start.is_some() && timer.end.is_some() { + let runtime_secs = timer + .end + .unwrap() + .saturating_duration_since(timer.start.unwrap()) + .as_secs_f32(); + summary.push_str(&format!("\n{0: <10} | {1: <10}s", timer.name, runtime_secs)) + } + } + summary + } +} + +/// The purpose of NamedTimer is to hold a name, +/// start Instant and end Instant for a specific timer. +/// The given name will be presented in the benchmark summary, +/// start and end Instants will be used for calculating runtime. +#[derive(Debug)] +pub struct NamedTimer { + name: &'static str, + start: Option, + end: Option, +} + +impl NamedTimer { + pub fn start(name: &'static str) -> Self { + Self { + name, + start: Some(Instant::now()), + end: None, + } + } + pub fn end(&mut self) { + self.end = Some(Instant::now()); + } +} + +#[test] +fn benchmark() { + let mut benchmarks = Benchmark::init(); + let mut test_timer = NamedTimer::start("test"); + std::thread::sleep(std::time::Duration::from_millis(100)); + test_timer.end(); + benchmarks.push(test_timer); + benchmarks.push(NamedTimer::start("only_start")); + assert!(benchmarks + .summary() + .contains("\nRustScan Benchmark Summary\ntest | 0.")); + assert_ne!(benchmarks.summary().contains("only_start"), true); +} diff --git a/src/input.rs b/src/input.rs new file mode 100644 index 0000000..d0ea366 --- /dev/null +++ b/src/input.rs @@ -0,0 +1,353 @@ +use serde_derive::Deserialize; +use std::collections::HashMap; +use std::fs; +use structopt::{clap::arg_enum, StructOpt}; + +const LOWEST_PORT_NUMBER: u16 = 1; +const TOP_PORT_NUMBER: u16 = 65535; + +arg_enum! { + /// Represents the strategy in which the port scanning will run. + /// - Serial will run from start to end, for example 1 to 1_000. + /// - Random will randomize the order in which ports will be scanned. + #[derive(Deserialize, Debug, StructOpt, Clone, Copy, PartialEq)] + 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 range of ports to be scanned. +#[derive(Deserialize, Debug, Clone, PartialEq)] +pub struct PortRange { + pub start: u16, + pub end: u16, +} + +#[cfg(not(tarpaulin_include))] +fn parse_range(input: &str) -> Result { + let range = input + .split('-') + .map(str::parse) + .collect::, std::num::ParseIntError>>(); + + if range.is_err() { + return Err(String::from( + "the range format must be 'start-end'. Example: 1-1000.", + )); + } + + match range.unwrap().as_slice() { + [start, end] => Ok(PortRange { + start: *start, + end: *end, + }), + _ => Err(String::from( + "the range format must be 'start-end'. Example: 1-1000.", + )), + } +} + +#[derive(StructOpt, Debug, Clone)] +#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)] +#[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 +pub struct Opts { + /// A list of comma separated CIDRs, IPs, or hosts to be scanned. + #[structopt(short, long, use_delimiter = true)] + pub addresses: Vec, + + /// A list of comma separed ports to be scanned. Example: 80,443,8080. + #[structopt(short, long, use_delimiter = true)] + pub ports: Option>, + + /// 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, + + /// Whether to ignore the configuration file or not. + #[structopt(short, long)] + pub no_config: bool, + + /// Greppable mode. Only output the ports. No Nmap. Useful for grep or outputting to a file. + #[structopt(short, long)] + pub greppable: bool, + + /// Accessible mode. Turns off features which negatively affect screen readers. + #[structopt(long)] + pub accessible: bool, + + /// The batch size for port scanning, it increases or slows the speed of + /// scanning. Depends on the open file limit of your OS. If you do 65535 + /// it will do every port at the same time. Although, your OS may not + /// support this. + #[structopt(short, long, default_value = "4500")] + pub batch_size: u16, + + /// The timeout in milliseconds before a port is assumed to be closed. + #[structopt(short, long, default_value = "1500")] + pub timeout: u32, + + /// 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")] + pub tries: u8, + + /// Automatically ups the ULIMIT with the value you provided. + #[structopt(short, long)] + pub ulimit: Option, + + /// The order of scanning to be performed. The "serial" option will + /// scan ports in ascending order while the "random" option will scan + /// ports randomly. + #[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")] + pub scan_order: ScanOrder, + + /// Level of scripting required for the run. + #[structopt(long, possible_values = &ScriptsRequired::variants(), case_insensitive = true, default_value = "default")] + pub scripts: ScriptsRequired, + + /// Use the top 1000 ports. + #[structopt(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'. + /// This command adds -Pn -vvv -p $PORTS automatically to nmap. + /// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"") + #[structopt(last = true)] + pub command: Vec, +} + +#[cfg(not(tarpaulin_include))] +impl Opts { + pub fn read() -> Self { + let mut opts = Opts::from_args(); + + if opts.ports.is_none() && opts.range.is_none() { + opts.range = Some(PortRange { + start: LOWEST_PORT_NUMBER, + end: TOP_PORT_NUMBER, + }); + } + + opts + } + + /// Reads the command line arguments into an Opts struct and merge + /// values found within the user configuration file. + pub fn merge(&mut self, config: &Config) { + if !self.no_config { + self.merge_required(&config); + self.merge_optional(&config); + } + } + + fn merge_required(&mut self, config: &Config) { + macro_rules! merge_required { + ($($field: ident),+) => { + $( + if let Some(e) = &config.$field { + self.$field = e.clone(); + } + )+ + } + } + + merge_required!( + addresses, greppable, accessible, batch_size, timeout, tries, scan_order, scripts, + command + ); + } + + fn merge_optional(&mut self, config: &Config) { + macro_rules! merge_optional { + ($($field: ident),+) => { + $( + if config.$field.is_some() { + self.$field = config.$field.clone(); + } + )+ + } + } + + // Only use top ports when the user asks for them + if self.top && config.ports.is_some() { + let mut ports: Vec = Vec::with_capacity(config.ports.clone().unwrap().len()); + for entry in config.ports.clone().unwrap().keys() { + ports.push(entry.parse().unwrap()) + } + self.ports = Some(ports); + } + + merge_optional!(range, ulimit); + } +} + +/// Struct used to deserialize the options specified within our config file. +/// These will be further merged with our command line arguments in order to +/// generate the final Opts struct. +#[cfg(not(tarpaulin_include))] +#[derive(Debug, Deserialize)] +pub struct Config { + addresses: Option>, + ports: Option>, + range: Option, + greppable: Option, + accessible: Option, + batch_size: Option, + timeout: Option, + tries: Option, + ulimit: Option, + scan_order: Option, + command: Option>, + scripts: Option, +} + +#[cfg(not(tarpaulin_include))] +impl Config { + /// Reads the configuration file with TOML format and parses it into a + /// Config struct. + /// + /// # Format + /// + /// addresses = ["127.0.0.1", "127.0.0.1"] + /// ports = [80, 443, 8080] + /// greppable = true + /// scan_order: "Serial" + /// + pub fn read() -> Self { + let mut home_dir = match dirs::home_dir() { + Some(dir) => dir, + None => panic!("Could not infer config file path."), + }; + home_dir.push(".rustscan.toml"); + + let mut content = String::new(); + if home_dir.exists() { + content = match fs::read_to_string(home_dir) { + Ok(content) => content, + Err(_) => String::new(), + } + } + + let config: Config = match toml::from_str(&content) { + Ok(config) => config, + Err(e) => { + println!("Found {} in configuration file.\nAborting scan.\n", e); + std::process::exit(1); + } + }; + + config + } +} + +#[cfg(test)] +mod tests { + use super::{Config, Opts, PortRange, ScanOrder, ScriptsRequired}; + impl Config { + fn default() -> Self { + Self { + addresses: Some(vec!["127.0.0.1".to_owned()]), + ports: None, + range: None, + greppable: Some(true), + batch_size: Some(25_000), + timeout: Some(1_000), + tries: Some(1), + ulimit: None, + command: Some(vec!["-A".to_owned()]), + accessible: Some(true), + scan_order: Some(ScanOrder::Random), + scripts: None, + } + } + } + + impl Opts { + pub fn default() -> Self { + Self { + addresses: vec![], + ports: None, + range: None, + greppable: true, + batch_size: 0, + timeout: 0, + tries: 0, + ulimit: None, + command: vec![], + accessible: false, + scan_order: ScanOrder::Serial, + no_config: true, + top: false, + scripts: ScriptsRequired::Default, + } + } + } + + #[test] + fn opts_no_merge_when_config_is_ignored() { + let mut opts = Opts::default(); + let config = Config::default(); + + opts.merge(&config); + + assert_eq!(opts.addresses, vec![] as Vec); + assert_eq!(opts.greppable, true); + assert_eq!(opts.accessible, false); + assert_eq!(opts.timeout, 0); + assert_eq!(opts.command, vec![] as Vec); + assert_eq!(opts.scan_order, ScanOrder::Serial); + } + + #[test] + fn opts_merge_required_arguments() { + let mut opts = Opts::default(); + let config = Config::default(); + + opts.merge_required(&config); + + assert_eq!(opts.addresses, config.addresses.unwrap()); + assert_eq!(opts.greppable, config.greppable.unwrap()); + assert_eq!(opts.timeout, config.timeout.unwrap()); + assert_eq!(opts.command, config.command.unwrap()); + assert_eq!(opts.accessible, config.accessible.unwrap()); + assert_eq!(opts.scan_order, config.scan_order.unwrap()); + 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.ulimit = Some(1_000); + + opts.merge_optional(&config); + + assert_eq!(opts.range, config.range); + assert_eq!(opts.ulimit, config.ulimit); + } +} diff --git a/src/main.rs b/src/main.rs index c6eb1a8..25490ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,120 +1,117 @@ +#![deny(clippy::all)] +#![warn(clippy::pedantic)] +#![allow(clippy::doc_markdown, clippy::if_not_else, clippy::non_ascii_literal)] + extern crate shell_words; mod tui; +mod input; +use input::{Config, Opts, PortRange, ScanOrder, ScriptsRequired}; + mod scanner; use scanner::Scanner; mod port_strategy; use port_strategy::PortStrategy; -use colorful::Color; -use colorful::Colorful; +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::Resource; -use rlimit::{getrlimit, setrlimit}; +use rlimit::{getrlimit, setrlimit, RawRlim, Resource, Rlim}; use std::collections::HashMap; -use std::process::Command; -use std::{net::IpAddr, time::Duration}; -use structopt::{clap::arg_enum, StructOpt}; +use std::convert::TryInto; +use std::fs::File; +use std::io::{prelude::*, BufReader}; +use std::net::{IpAddr, ToSocketAddrs}; +use std::path::Path; +use std::string::ToString; +use std::time::Duration; +use trust_dns_resolver::{ + config::{ResolverConfig, ResolverOpts}, + Resolver, +}; extern crate colorful; extern crate dirs; -const LOWEST_PORT_NUMBER: u16 = 1; -const TOP_PORT_NUMBER: u16 = 65535; // Average value for Ubuntu -const DEFAULT_FILE_DESCRIPTORS_LIMIT: rlimit::rlim = 8000; +const DEFAULT_FILE_DESCRIPTORS_LIMIT: RawRlim = 8000; // Safest batch size based on experimentation -const AVERAGE_BATCH_SIZE: rlimit::rlim = 3000; +const AVERAGE_BATCH_SIZE: RawRlim = 3000; #[macro_use] extern crate log; -arg_enum! { - #[derive(Debug, StructOpt)] - pub enum ScanOrder { - Serial, - Random, - } -} - -#[derive(StructOpt, Debug)] -#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)] -/// Fast Port Scanner built in Rust. -/// WARNING Do not use this program against sensitive infrastructure since the -/// specified server may not be able to handle this many socket connections at once. -/// - Discord https://discord.gg/GFrQsGy -/// - GitHub https://github.com/RustScan/RustScan -struct Opts { - /// A list of comma separated IP addresses to be scanned. - #[structopt(use_delimiter = true, parse(try_from_str), required = true)] - ips: Vec, - - ///Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file. - #[structopt(short, long)] - quiet: bool, - - //Accessible mode. Turns off features which negatively affect screen readers. - #[structopt(short, long)] - accessible: bool, - - /// The batch size for port scanning, it increases or slows the speed of - /// scanning. Depends on the open file limit of your OS. If you do 65535 - /// it will do every port at the same time. Although, your OS may not - /// support this. - #[structopt(short, long, default_value = "4500")] - batch_size: u16, - - /// The timeout in milliseconds before a port is assumed to be closed. - #[structopt(short, long, default_value = "1500")] - timeout: u32, - - /// Automatically ups the ULIMIT with the value you provided. - #[structopt(short, long)] - ulimit: Option, - - /// 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")] - scan_order: ScanOrder, - - /// The Nmap arguments to run. - /// To use the argument -A, end RustScan's args with '-- -A'. - /// Example: 'rustscan -T 1500 127.0.0.1 -- -A -sC'. - /// This command adds -Pn -vvv -p $PORTS automatically to nmap. - /// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"") - #[structopt(last = true)] - command: Vec, -} - #[cfg(not(tarpaulin_include))] +#[allow(clippy::too_many_lines)] /// Faster Nmap scanning with Rust /// If you're looking for the actual scanning, check out the module Scanner fn main() { env_logger::init(); + let mut benchmarks = Benchmark::init(); + let mut rustscan_bench = NamedTimer::start("RustScan"); - info!("Starting up"); - let opts = Opts::from_args(); - info!("Mains() `opts` arguments are {:?}", opts); + let mut opts: Opts = Opts::read(); + let config = Config::read(); + opts.merge(&config); - if !opts.quiet && !opts.accessible { - print_opening(); + debug!("Main() `opts` arguments are {:?}", opts); + + let scripts_to_run: Vec = match init_scripts(opts.scripts) { + Ok(scripts_to_run) => scripts_to_run, + Err(e) => { + warning!( + format!("Initiating scripts failed!\n{}", e), + opts.greppable, + opts.accessible + ); + std::process::exit(1); + } + }; + + debug!("Scripts initialized {:?}", &scripts_to_run); + + if !opts.greppable && !opts.accessible { + print_opening(&opts); } - let ulimit: rlimit::rlim = adjust_ulimit_size(&opts); + let ips: Vec = parse_addresses(&opts); + + if ips.is_empty() { + warning!( + "No IPs could be resolved, aborting scan.", + opts.greppable, + opts.accessible + ); + std::process::exit(1); + } + + let ulimit: RawRlim = adjust_ulimit_size(&opts); let batch_size: u16 = infer_batch_size(&opts, ulimit); let scanner = Scanner::new( - &opts.ips, + &ips, batch_size, Duration::from_millis(opts.timeout.into()), - opts.quiet, - PortStrategy::pick(LOWEST_PORT_NUMBER, TOP_PORT_NUMBER, opts.scan_order), + opts.tries, + opts.greppable, + PortStrategy::pick(&opts.range, opts.ports, opts.scan_order), + opts.accessible, ); + debug!("Scanner finished building: {:?}", scanner); + let mut portscan_bench = NamedTimer::start("Portscan"); let scan_result = block_on(scanner.run()); + portscan_bench.end(); + benchmarks.push(portscan_bench); + let mut ports_per_ip = HashMap::new(); for socket in scan_result { @@ -124,7 +121,7 @@ fn main() { .push(socket.port()); } - for ip in opts.ips { + for ip in ips { if ports_per_ip.contains_key(&ip) { continue; } @@ -132,59 +129,87 @@ fn main() { // If we got here it means the IP was not found within the HashMap, this // means the scan couldn't find any open ports for it. - let x = format!("{} Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size. - \n*I used {} batch size, consider lowering to {} with {} or a comfortable number for your system. - \n Alternatively, increase the timeout if your ping is high. Rustscan -T 2000 for 2000 second timeout.\n", - "ERROR", + let x = format!("Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size. + \n*I used {} batch size, consider lowering it with {} or a comfortable number for your system. + \n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n", ip, opts.batch_size, - (opts.batch_size / 2).to_string(), - "'rustscan -b '"); - warning!(x, opts.quiet); + "'rustscan -b -a '"); + warning!(x, opts.greppable, opts.accessible); } - for (ip, ports) in ports_per_ip.iter_mut() { - let nmap_str_ports: Vec = ports.into_iter().map(|port| port.to_string()).collect(); - - detail!("Starting Nmap", opts.quiet); + let mut script_bench = NamedTimer::start("Scripts"); + for (ip, ports) in &ports_per_ip { + let vec_str_ports: Vec = ports.iter().map(ToString::to_string).collect(); // nmap port style is 80,443. Comma separated with no spaces. - let ports_str = nmap_str_ports.join(","); + let ports_str = vec_str_ports.join(","); - // if quiet mode is on nmap should not be spawned - if opts.quiet { - println!("{}", ports_str); + // if option scripts is none, no script will be spawned + if opts.greppable || opts.scripts == ScriptsRequired::None { + println!("{} -> [{}]", &ip, ports_str); continue; } + detail!("Starting Script(s)", opts.greppable, opts.accessible); - let addr = ip.to_string(); - let user_nmap_args = - shell_words::split(&opts.command.join(" ")).expect("failed to parse nmap arguments"); - let nmap_args = build_nmap_arguments(&addr, &ports_str, &user_nmap_args, ip.is_ipv6()); + // 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() { + // 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); + if script_f.call_format.is_some() { + let mut call_f = script_f.call_format.unwrap(); + call_f.push(' '); + call_f.push_str(user_extra_args); + output!( + format!("Running script {:?} on ip {}\nDepending on the complexity of the script, results may take some time to appear.", call_f, &ip), + opts.greppable, + opts.accessible + ); + debug!("Call format {}", call_f); + script_f.call_format = Some(call_f); + } + } - output!(format!( - "The Nmap command to be run is nmap {}\n", - &nmap_args.join(" ") - )); - - // Runs the nmap command and spawns it as a process. - let mut child = Command::new("nmap") - .args(&nmap_args) - .spawn() - .expect("failed to execute nmap process"); - - child.wait().expect("failed to wait on nmap process"); + // Building the script with the arguments from the ScriptFile, and ip-ports. + let script = Script::build( + script_f.path, + *ip, + ports.clone(), + script_f.port, + script_f.ports_separator, + script_f.tags, + script_f.call_format, + ); + match script.run() { + Ok(script_result) => { + detail!(script_result.to_string(), opts.greppable, opts.accessible); + } + Err(e) => { + warning!(&format!("Error {}", e), opts.greppable, opts.accessible); + } + } + } } + + // 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); + info!("{}", benchmarks.summary()); } /// Prints the opening title of RustScan -fn print_opening() { - info!("Printing opening"); +fn print_opening(opts: &Opts) { + debug!("Printing opening"); let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-. | {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| | | .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ | `-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-' -Faster Nmap scanning with Rust."#; +The Modern Day Port Scanner."#; println!("{}", s.gradient(Color::Green).bold()); let info = r#"________________________________________ : https://discord.gg/GFrQsGy : @@ -193,69 +218,146 @@ Faster Nmap scanning with Rust."#; println!("{}", info.gradient(Color::Yellow).bold()); funny_opening!(); - let config_path = match dirs::config_dir() { - Some(mut path) => { - path.push("rustscan"); - path.push("config.toml"); - path - } - None => panic!("Couldn't find config dir."), - }; + let config_path = dirs::home_dir() + .expect("Could not infer config file path.") + .join(".rustscan.toml"); - detail!(format!( - "{} {:?}", - "The config file is expected to be at", config_path - )); + detail!( + format!("The config file is expected to be at {:?}", config_path), + opts.greppable, + opts.accessible + ); } -#[cfg(not(tarpaulin_include))] -fn build_nmap_arguments<'a>( - addr: &'a str, - ports: &'a str, - user_args: &'a Vec, - is_ipv6: bool, -) -> Vec<&'a str> { - let mut arguments: Vec<&str> = user_args.iter().map(AsRef::as_ref).collect(); - arguments.push("-A"); - arguments.push("-vvv"); - if is_ipv6 { - arguments.push("-6"); +/// Goes through all possible IP inputs (files or via argparsing) +/// Parses the string(s) into IPs +fn parse_addresses(input: &Opts) -> Vec { + let mut ips: Vec = 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); + } } - arguments.push("-p"); - arguments.push(ports); - arguments.push(addr); + // 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); - arguments + 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 } -fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim { - if opts.ulimit.is_some() { - let limit: rlimit::rlim = opts.ulimit.unwrap(); +/// 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 { + 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)) +} - match setrlimit(Resource::NOFILE, limit, limit) { - Ok(_) => { - detail!( - format!("Automatically increasing ulimit value to {}.", limit), - opts.quiet - ); - } - Err(_) => println!("{}", "ERROR. Failed to set ulimit value."), +/// Uses DNS to get the IPS assiocated with host +fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec { + let mut ips: Vec = 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, std::io::Error> { + let file = File::open(ips)?; + let reader = BufReader::new(file); + + let mut ips: Vec = 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() { + detail!( + format!("Automatically increasing ulimit value to {}.", limit), + opts.greppable, + opts.accessible + ); + } else { + warning!( + "ERROR. Failed to set ulimit value.", + opts.greppable, + opts.accessible + ); } } let (rlim, _) = getrlimit(Resource::NOFILE).unwrap(); - rlim + rlim.as_raw() } -fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 { - let mut batch_size: rlimit::rlim = opts.batch_size.into(); +fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 { + let mut batch_size: RawRlim = opts.batch_size.into(); // Adjust the batch size when the ulimit value is lower than the desired batch size if ulimit < batch_size { - warning!("File limit is lower than default batch size. Consider upping with --ulimt. May cause harm to sensitive servers", - opts.quiet + warning!("File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers", + opts.greppable, opts.accessible ); // When the OS supports high file limits like 8000, but the user @@ -265,62 +367,46 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 { // ulimit is smaller than aveage batch size // user must have very small ulimit // decrease batch size to half of ulimit - warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. "); + warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. ", opts.greppable, opts.accessible); info!("Halving batch_size because ulimit is smaller than average batch size"); - batch_size = ulimit / 2 + batch_size = ulimit / 2; } else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT { info!("Batch size is now average batch size"); - batch_size = AVERAGE_BATCH_SIZE + batch_size = AVERAGE_BATCH_SIZE; } else { - batch_size = ulimit - 100 + batch_size = ulimit - 100; } } // When the ulimit is higher than the batch size let the user know that the // batch size can be increased unless they specified the ulimit themselves. else if ulimit + 2 > batch_size && (opts.ulimit.is_none()) { - detail!(format!( - "File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", - ulimit - 100 - ), opts.quiet); + detail!(format!("File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", ulimit - 100), + opts.greppable, opts.accessible); } - batch_size as u16 + 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, print_opening, Opts, ScanOrder}; - use std::{net::IpAddr, str::FromStr}; + use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts}; + use std::net::Ipv4Addr; #[test] fn batch_size_lowered() { - let opts = Opts { - ips: vec![IpAddr::from_str("127.0.0.1").unwrap()], - quiet: true, - batch_size: 50_000, - timeout: 1_000, - ulimit: Some(2_000), - command: Vec::new(), - accessible: false, - scan_order: ScanOrder::Serial, - }; + let mut opts = Opts::default(); + opts.batch_size = 50_000; let batch_size = infer_batch_size(&opts, 120); - assert!(batch_size < 50_000); + assert!(batch_size < opts.batch_size); } #[test] fn batch_size_lowered_average_size() { - let opts = Opts { - ips: vec![IpAddr::from_str("127.0.0.1").unwrap()], - quiet: true, - batch_size: 50_000, - timeout: 1_000, - ulimit: Some(2_000), - command: Vec::new(), - accessible: false, - scan_order: ScanOrder::Serial, - }; + let mut opts = Opts::default(); + opts.batch_size = 50_000; let batch_size = infer_batch_size(&opts, 9_000); assert!(batch_size == 3_000); @@ -329,16 +415,8 @@ mod tests { fn batch_size_equals_ulimit_lowered() { // because ulimit and batch size are same size, batch size is lowered // to ULIMIT - 100 - let opts = Opts { - ips: vec![IpAddr::from_str("127.0.0.1").unwrap()], - quiet: true, - batch_size: 50_000, - timeout: 1_000, - ulimit: Some(2_000), - command: Vec::new(), - accessible: false, - scan_order: ScanOrder::Serial, - }; + let mut opts = Opts::default(); + opts.batch_size = 50_000; let batch_size = infer_batch_size(&opts, 5_000); assert!(batch_size == 4_900); @@ -346,41 +424,100 @@ mod tests { #[test] fn batch_size_adjusted_2000() { // ulimit == batch_size - let opts = Opts { - ips: vec![IpAddr::from_str("127.0.0.1").unwrap()], - quiet: true, - batch_size: 50_000, - timeout: 1_000, - ulimit: Some(2_000), - command: Vec::new(), - accessible: false, - scan_order: ScanOrder::Serial, - }; + let mut opts = Opts::default(); + opts.batch_size = 50_000; + opts.ulimit = Some(2_000); let batch_size = adjust_ulimit_size(&opts); assert!(batch_size == 2_000); } #[test] fn test_print_opening_no_panic() { + let mut opts = Opts::default(); + opts.ulimit = Some(2_000); // print opening should not panic - print_opening(); - assert!(1 == 1); + print_opening(&opts); + } + + #[test] + fn test_high_ulimit_no_greppable_mode() { + let mut opts = Opts::default(); + opts.batch_size = 10; + opts.greppable = false; + + let batch_size = infer_batch_size(&opts, 1_000_000); + + assert!(batch_size == opts.batch_size); + } + + #[test] + fn parse_correct_addresses() { + let mut opts = Opts::default(); + opts.addresses = vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()]; + let ips = parse_addresses(&opts); + + assert_eq!( + ips, + [ + Ipv4Addr::new(127, 0, 0, 1), + Ipv4Addr::new(192, 168, 0, 0), + Ipv4Addr::new(192, 168, 0, 1), + Ipv4Addr::new(192, 168, 0, 2), + Ipv4Addr::new(192, 168, 0, 3) + ] + ); + } + + #[test] + fn parse_correct_host_addresses() { + let mut opts = Opts::default(); + opts.addresses = vec!["google.com".to_owned()]; + let ips = parse_addresses(&opts); + + assert_eq!(ips.len(), 1); + } + + #[test] + fn parse_correct_and_incorrect_addresses() { + let mut opts = Opts::default(); + opts.addresses = vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()]; + let ips = parse_addresses(&opts); + + assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]); + } + + #[test] + fn parse_incorrect_addresses() { + let mut opts = Opts::default(); + opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()]; + let ips = parse_addresses(&opts); + + assert_eq!(ips.is_empty(), true); } #[test] - fn test_high_ulimit_no_quiet_mode() { - let opts = Opts { - ips: vec![IpAddr::from_str("127.0.0.1").unwrap()], - quiet: false, - batch_size: 10, - timeout: 1_000, - ulimit: None, - command: Vec::new(), - accessible: true, - scan_order: ScanOrder::Serial, - }; + 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); + } - infer_batch_size(&opts, 1_000_000); + #[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); + } - assert!(1 == 1); + #[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); } } diff --git a/src/port_strategy/mod.rs b/src/port_strategy/mod.rs index aedf9f8..579446a 100644 --- a/src/port_strategy/mod.rs +++ b/src/port_strategy/mod.rs @@ -1,26 +1,50 @@ mod range_iterator; -use super::ScanOrder; +use super::{PortRange, ScanOrder}; +use rand::seq::SliceRandom; +use rand::thread_rng; use range_iterator::RangeIterator; /// Represents options of port scanning. /// /// Right now all these options involve ranges, but in the future /// it will also contain custom lists of ports. +#[derive(Debug)] pub enum PortStrategy { + Manual(Vec), Serial(SerialRange), Random(RandomRange), } impl PortStrategy { - pub fn pick(start: u16, end: u16, scan_type: ScanOrder) -> Self { - match scan_type { - ScanOrder::Serial => PortStrategy::Serial(SerialRange { start, end }), - ScanOrder::Random => PortStrategy::Random(RandomRange { start, end }), + pub fn pick(range: &Option, ports: Option>, order: ScanOrder) -> Self { + match order { + ScanOrder::Serial if ports.is_none() => { + let range = range.as_ref().unwrap(); + PortStrategy::Serial(SerialRange { + start: range.start, + end: range.end, + }) + } + ScanOrder::Random if ports.is_none() => { + let range = range.as_ref().unwrap(); + PortStrategy::Random(RandomRange { + start: range.start, + end: range.end, + }) + } + ScanOrder::Serial => PortStrategy::Manual(ports.unwrap()), + ScanOrder::Random => { + let mut rng = thread_rng(); + let mut ports = ports.unwrap(); + ports.shuffle(&mut rng); + PortStrategy::Manual(ports) + } } } pub fn order(&self) -> Vec { match self { + PortStrategy::Manual(ports) => ports.to_vec(), PortStrategy::Serial(range) => range.generate(), PortStrategy::Random(range) => range.generate(), } @@ -35,6 +59,7 @@ trait RangeOrder { /// As the name implies SerialRange will always generate a vector in /// ascending order. +#[derive(Debug)] pub struct SerialRange { start: u16, end: u16, @@ -48,6 +73,7 @@ impl RangeOrder for SerialRange { /// As the name implies RandomRange will always generate a vector with /// a random order. This vector is built following the LCG algorithm. +#[derive(Debug)] pub struct RandomRange { start: u16, end: u16, @@ -71,18 +97,20 @@ impl RangeOrder for RandomRange { #[cfg(test)] mod tests { use super::PortStrategy; - use crate::ScanOrder; + use crate::{PortRange, ScanOrder}; #[test] - fn serial_strategy() { - let strategy = PortStrategy::pick(1, 100, ScanOrder::Serial); + fn serial_strategy_with_range() { + let range = PortRange { start: 1, end: 100 }; + let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Serial); let result = strategy.order(); let expected_range = (1..100).into_iter().collect::>(); assert_eq!(expected_range, result); } #[test] - fn random_strategy() { - let strategy = PortStrategy::pick(1, 100, ScanOrder::Random); + fn random_strategy_with_range() { + let range = PortRange { start: 1, end: 100 }; + let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); let mut result = strategy.order(); let expected_range = (1..100).into_iter().collect::>(); assert_ne!(expected_range, result); @@ -90,4 +118,22 @@ mod tests { result.sort(); assert_eq!(expected_range, result); } + + #[test] + fn serial_strategy_with_ports() { + let strategy = PortStrategy::pick(&None, Some(vec![80, 443]), ScanOrder::Serial); + let result = strategy.order(); + assert_eq!(vec![80, 443], result); + } + + #[test] + fn random_strategy_with_ports() { + let strategy = PortStrategy::pick(&None, Some((1..10).collect()), ScanOrder::Random); + let mut result = strategy.order(); + let expected_range = (1..10).into_iter().collect::>(); + assert_ne!(expected_range, result); + + result.sort(); + assert_eq!(expected_range, result); + } } diff --git a/src/port_strategy/range_iterator.rs b/src/port_strategy/range_iterator.rs index a202efd..d8fa750 100644 --- a/src/port_strategy/range_iterator.rs +++ b/src/port_strategy/range_iterator.rs @@ -1,5 +1,6 @@ use gcd::Gcd; use rand::Rng; +use std::convert::TryInto; pub struct RangeIterator { active: bool, @@ -60,7 +61,11 @@ impl Iterator for RangeIterator { } self.normalized_pick = next_pick; - Some((self.actual_start + current_pick) as u16) + Some( + (self.actual_start + current_pick) + .try_into() + .expect("Could not convert u32 to u16"), + ) } } diff --git a/src/scanner/mod.rs b/src/scanner/mod.rs index fea8ada..d30521c 100644 --- a/src/scanner/mod.rs +++ b/src/scanner/mod.rs @@ -1,45 +1,56 @@ use super::PortStrategy; +mod socket_iterator; +use socket_iterator::SocketIterator; + use async_std::io; use async_std::net::TcpStream; use async_std::prelude::*; -use colored::*; +use colored::Colorize; use futures::stream::FuturesUnordered; -use std::time::Duration; use std::{ - io::ErrorKind, + collections::HashSet, net::{IpAddr, Shutdown, SocketAddr}, + num::NonZeroU8, + time::Duration, }; /// The class for the scanner -/// Host is data type IpAddr and is the host address +/// IP is data type IpAddr and is the IP address /// start & end is where the port scan starts and ends /// batch_size is how many ports at a time should be scanned /// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration. -/// Quiet is whether or not RustScan should print things, or wait until the end to print only open ports. +/// greppable is whether or not RustScan should print things, or wait until the end to print only the ip and open ports. #[cfg(not(tarpaulin_include))] +#[derive(Debug)] pub struct Scanner { - hosts: Vec, + ips: Vec, batch_size: u16, timeout: Duration, - quiet: bool, + tries: NonZeroU8, + greppable: bool, port_strategy: PortStrategy, + accessible: bool, } impl Scanner { pub fn new( - hosts: &[IpAddr], + ips: &[IpAddr], batch_size: u16, timeout: Duration, - quiet: bool, + tries: u8, + greppable: bool, port_strategy: PortStrategy, + accessible: bool, ) -> Self { Self { batch_size, timeout, - quiet, + tries: NonZeroU8::new(std::cmp::max(tries, 1)).unwrap(), + greppable, port_strategy, - hosts: hosts.iter().map(|host| host.to_owned()).collect(), + ips: ips.iter().map(ToOwned::to_owned).collect(), + accessible, } } @@ -48,80 +59,106 @@ impl Scanner { /// Returns all open ports as Vec pub async fn run(&self) -> Vec { let ports: Vec = self.port_strategy.order(); - let batch_per_host: usize = self.batch_size as usize / self.hosts.len(); + let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports); let mut open_sockets: Vec = Vec::new(); - - for batch in ports.chunks(batch_per_host) { - let mut sockets = self.scan_ports(batch).await; - open_sockets.append(&mut sockets); - } - - open_sockets - } - - /// Given a slice of sockets, scan them all. - /// Returns a vector of open sockets. - async fn scan_ports(&self, ports: &[u16]) -> Vec { let mut ftrs = FuturesUnordered::new(); - for port in ports { - for host in &self.hosts { - ftrs.push(self.scan_socket(SocketAddr::new(*host, *port))); + let mut errors: HashSet = 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)); + } else { + break; } } - let mut open_sockets: Vec = Vec::new(); + debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ", + self.batch_size, + self.ips.len(), + &ports.len(), + (self.ips.len() * ports.len())); + while let Some(result) = ftrs.next().await { + if let Some(socket) = socket_iterator.next() { + ftrs.push(self.scan_socket(socket)); + } + match result { Ok(socket) => open_sockets.push(socket), - _ => {} + Err(e) => { + let error_string = e.to_string(); + if errors.len() < self.ips.len() * 1000 { + errors.insert(error_string); + } + } } } - + debug!("Typical socket connection errors {:?}", errors); + debug!("Open Sockets found: {:?}", &open_sockets); open_sockets } - /// Given a port, scan it. + /// Given a socket, scan it self.tries times. /// Turns the address into a SocketAddr /// Deals with the type /// If it experiences error ErrorKind::Other then too many files are open and it Panics! - /// ese any other error, it returns the error in Result as a string - /// If no errors occur, it returns the port number in Result to signify the port is open. + /// 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_port(10:u16) + /// self.scan_socket(socket) /// - /// Note: `self` must contain `self.host`. + /// Note: `self` must contain `self.ip`. async fn scan_socket(&self, socket: SocketAddr) -> io::Result { - match self.connect(socket).await { - Ok(x) => { - // match stream_result.shutdown(Shutdown::Both) - info!("Shutting down stream"); - match x.shutdown(Shutdown::Both) { - _ => {} - } - if !self.quiet { - println!("Open {}", socket.to_string().purple()); - } + let tries = self.tries.get(); - Ok(socket) - } - Err(e) => match e.kind() { - ErrorKind::Other => { - panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500."); + 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()); + } + } + + debug!("Return Ok after {} tries", nr_try); + return Ok(socket); } - _ => Err(io::Error::new(io::ErrorKind::Other, e.to_string())), - }, + Err(e) => { + let mut error_string = e.to_string(); + + if error_string.to_lowercase().contains("too many open files") { + panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500."); + } + + 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)); + } + } + }; } + unreachable!(); } /// Performs the connection to the socket with timeout /// # Example /// /// let port: u16 = 80 - /// // Host is an IpAddr type - /// let host = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// let socket = SocketAddr::new(host, port); + /// // 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 @@ -139,7 +176,7 @@ impl Scanner { #[cfg(test)] mod tests { use super::*; - use crate::ScanOrder; + use crate::{PortRange, ScanOrder}; use async_std::task::block_on; use std::{net::IpAddr, time::Duration}; @@ -147,8 +184,20 @@ mod tests { fn scanner_runs() { // Makes sure the program still runs and doesn't panic let addrs = vec!["127.0.0.1".parse::().unwrap()]; - let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy); + 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); @@ -157,8 +206,20 @@ mod tests { fn ipv6_scanner_runs() { // Makes sure the program still runs and doesn't panic let addrs = vec!["::1".parse::().unwrap()]; - let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy); + 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); @@ -166,16 +227,40 @@ mod tests { #[test] fn quad_zero_scanner_runs() { let addrs = vec!["0.0.0.0".parse::().unwrap()]; - let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy); + 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::().unwrap()]; - let strategy = PortStrategy::pick(400, 445, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy); + 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); } @@ -185,8 +270,20 @@ mod tests { let addrs = vec!["8.8.8.8".parse::().unwrap()]; // mac should have this automatically scaled down - let strategy = PortStrategy::pick(400, 600, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy); + 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); } diff --git a/src/scanner/socket_iterator.rs b/src/scanner/socket_iterator.rs new file mode 100644 index 0000000..1c71466 --- /dev/null +++ b/src/scanner/socket_iterator.rs @@ -0,0 +1,75 @@ +use itertools::{iproduct, Product}; +use std::net::{IpAddr, SocketAddr}; + +pub struct SocketIterator<'s> { + // product_it is a cartesian product iterator over + // the slices of ports and IP addresses. + // + // The IP/port order is intentionally reversed here since we want + // the itertools::iproduct! macro below to generate the pairs with + // all the IPs for one port before moving on to the next one + // ("hold the port, go through all the IPs, then advance the port..."). + // See also the comments in the iterator implementation for an example. + product_it: + Product>, Box>>, +} + +/// 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), + } + } +} + +impl<'s> Iterator for SocketIterator<'s> { + type Item = SocketAddr; + + /// Returns a socket based on the combination of one of the provided + /// IPs and ports or None when these combinations are exhausted. Every + /// IP will have the same port until a port is incremented. + /// + /// let it = SocketIterator::new(&["127.0.0.1", "192.168.0.1"], &[80, 443]); + /// it.next(); // 127.0.0.1:80 + /// it.next(); // 192.168.0.1:80 + /// it.next(); // 127.0.0.1:443 + /// it.next(); // 192.168.0.1:443 + /// it.next(); // None + fn next(&mut self) -> Option { + match self.product_it.next() { + None => None, + Some((port, ip)) => Some(SocketAddr::new(*ip, *port)), + } + } +} + +#[cfg(test)] +mod tests { + use super::SocketIterator; + use std::net::{IpAddr, SocketAddr}; + + #[test] + fn goes_through_every_ip_port_combination() { + let addrs = vec![ + "127.0.0.1".parse::().unwrap(), + "192.168.0.1".parse::().unwrap(), + ]; + let ports: Vec = vec![22, 80, 443]; + let mut it = SocketIterator::new(&addrs, &ports); + + assert_eq!(Some(SocketAddr::new(addrs[0], ports[0])), it.next()); + assert_eq!(Some(SocketAddr::new(addrs[1], ports[0])), it.next()); + assert_eq!(Some(SocketAddr::new(addrs[0], ports[1])), it.next()); + assert_eq!(Some(SocketAddr::new(addrs[1], ports[1])), it.next()); + assert_eq!(Some(SocketAddr::new(addrs[0], ports[2])), it.next()); + assert_eq!(Some(SocketAddr::new(addrs[1], ports[2])), it.next()); + assert_eq!(None, it.next()); + } +} diff --git a/src/scripts/mod.rs b/src/scripts/mod.rs new file mode 100644 index 0000000..a5b8689 --- /dev/null +++ b/src/scripts/mod.rs @@ -0,0 +1,466 @@ +//! 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 +//! +//! 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. +//! +//! 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 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. +//! +//! 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 +//! +//! 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 +//! +//! 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. +//! +//! 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 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::string::ToString; +use subprocess::{Exec, ExitStatus}; +use text_placeholder::Template; + +static DEFAULT: &str = r#"tags = ["core_approved", "RustScan", "default"] +developer = [ "RustScan", "https://github.com/RustScan" ] +ports_separator = "," +call_format = "nmap -vvv -p {{port}} {{ip}}" +"#; + +#[cfg(not(tarpaulin_include))] +pub fn init_scripts(scripts: ScriptsRequired) -> Result> { + let mut scripts_to_run: Vec = Vec::new(); + + match scripts { + ScriptsRequired::None => Ok(scripts_to_run), + ScriptsRequired::Default => { + let default_script = + toml::from_str::(&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_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 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); + + // Only Scripts that contain all the tags found in ScriptConfig will be selected. + if script_config.tags.is_some() { + let config_hashset: HashSet = + script_config.tags.unwrap().into_iter().collect(); + for script in &parsed_scripts { + if script.tags.is_some() { + let script_hashset: HashSet = + script.tags.clone().unwrap().into_iter().collect(); + if config_hashset.is_subset(&script_hashset) { + scripts_to_run.push(script.to_owned()); + } else { + debug!( + "\nScript tags does not match config tags {:?} {}", + &script_hashset, + script.path.clone().unwrap().display() + ); + } + } + } + } + debug!("\nScript(s) to run {:?}", scripts_to_run); + Ok(scripts_to_run) + } + } +} + +pub fn parse_scripts(scripts: Vec) -> Vec { + let mut parsed_scripts: Vec = Vec::with_capacity(scripts.len()); + for script in scripts { + debug!("Parsing script {}", &script.display()); + if let Some(script_file) = ScriptFile::new(script) { + parsed_scripts.push(script_file); + } + } + parsed_scripts +} + +#[derive(Clone, Debug)] +pub struct Script { + // Path to the script itself. + path: Option, + + // Ip got from scanner. + ip: IpAddr, + + // Ports found with portscan. + open_ports: Vec, + + // Port found in ScriptFile, if defined only this will run with the ip. + trigger_port: Option, + + // Character to join ports in case we want to use a string format of them, for example nmap -p. + ports_separator: Option, + + // Tags found in ScriptFile. + tags: Option>, + + // The format how we want the script to run. + call_format: Option, +} + +#[derive(Serialize)] +struct ExecPartsScript { + script: String, + ip: String, + port: String, +} + +#[derive(Serialize)] +struct ExecParts { + ip: String, + port: String, +} + +impl Script { + pub fn build( + path: Option, + ip: IpAddr, + open_ports: Vec, + trigger_port: Option, + ports_separator: Option, + tags: Option>, + call_format: Option, + ) -> Self { + Self { + path, + ip, + open_ports, + trigger_port, + ports_separator, + tags, + call_format, + } + } + + // Some variables get changed before read, and compiler throws warning on warn(unused_assignments) + #[allow(unused_assignments)] + pub fn run(self) -> Result { + debug!("run self {:?}", &self); + + let separator = self.ports_separator.unwrap_or_else(|| ",".into()); + + let mut ports_str = self + .open_ports + .iter() + .map(ToString::to_string) + .collect::>() + .join(&separator); + if let Some(port) = self.trigger_port { + ports_str = port; + } + + let mut final_call_format = String::new(); + if let Some(call_format) = self.call_format { + final_call_format = call_format; + } else { + return Err(anyhow!("Failed to parse execution format.")); + } + let default_template: Template = Template::new(&final_call_format); + let mut to_run = String::new(); + + if final_call_format.contains("{{script}}") { + let exec_parts_script: ExecPartsScript = ExecPartsScript { + script: self.path.unwrap().to_str().unwrap().to_string(), + ip: self.ip.to_string(), + port: ports_str, + }; + to_run = default_template.fill_with_struct(&exec_parts_script)?; + } else { + let exec_parts: ExecParts = ExecParts { + ip: self.ip.to_string(), + port: ports_str, + }; + 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) + } +} + +#[cfg(not(tarpaulin_include))] +fn execute_script(mut arguments: Vec) -> Result { + 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, + }; + if es != 0 { + return Err(anyhow!("Exit code = {}", es)); + } + Ok(c.stdout_str()) + } + Err(error) => { + debug!("Command error {}", error.to_string()); + Err(anyhow!(error.to_string())) + } + } +} + +pub fn find_scripts(mut path: PathBuf) -> Result> { + path.push(".rustscan_scripts"); + if path.is_dir() { + debug!("Scripts folder found {}", &path.display()); + let mut files_vec: Vec = Vec::new(); + for entry in fs::read_dir(path)? { + let entry = entry?; + files_vec.push(entry.path()); + } + Ok(files_vec) + } else { + Err(anyhow!("Can't find scripts folder {}", path.display())) + } +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ScriptFile { + pub path: Option, + pub tags: Option>, + pub developer: Option>, + pub port: Option, + pub ports_separator: Option, + pub call_format: Option, +} + +impl ScriptFile { + fn new(script: PathBuf) -> Option { + 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; + } + } + } + } else { + debug!("Failed to read file: {}", &real_path.display()); + return None; + } + debug!("ScriptFile {} lines\n{}", &real_path.display(), &lines_buf); + + match toml::from_str::(&lines_buf) { + Ok(mut 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()); + None + } + } + } +} + +#[derive(Debug, Deserialize, Clone)] +pub struct ScriptConfig { + pub tags: Option>, + pub ports: Option>, + pub developer: Option>, +} + +#[cfg(not(tarpaulin_include))] +impl ScriptConfig { + pub fn read_config() -> Result { + let mut home_dir = match dirs::home_dir() { + Some(dir) => dir, + None => return Err(anyhow!("Could not infer ScriptConfig path.")), + }; + home_dir.push(".rustscan_scripts.toml"); + + let content = fs::read_to_string(home_dir)?; + let config = toml::from_str::(&content)?; + Ok(config) + } +} + +#[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, + ) + } + + #[test] + fn find_and_parse_scripts() { + let scripts = find_scripts("fixtures/".into()).unwrap(); + let scripts = parse_scripts(scripts); + assert_eq!(scripts.len(), 4); + } + + #[test] + #[should_panic] + fn find_invalid_folder() { + let _scripts = find_scripts("Cargo.toml".into()).unwrap(); + } + + #[test] + #[should_panic] + fn open_script_file_invalid_headers() { + ScriptFile::new("fixtures/.rustscan_scripts/test_script_invalid_headers.txt".into()) + .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() { + ScriptFile::new("qwertyuiop.txt".into()).unwrap(); + } + + #[test] + fn parse_txt_script() { + let script_f = + ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap(); + assert_eq!( + script_f.tags, + Some(vec!["core_approved".to_string(), "example".to_string()]) + ); + assert_eq!( + script_f.developer, + Some(vec![ + "example".to_string(), + "https://example.org".to_string() + ]) + ); + assert_eq!(script_f.ports_separator, Some(",".to_string())); + assert_eq!( + script_f.call_format, + Some("nmap -vvv -p {{port}} {{ip}}".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"); + } + + #[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 + assert_eq!( + output.trim(), + "Python script ran with arguments ['fixtures/.rustscan_scripts/test_script.py', '127.0.0.1', '80,8080']" + ); + } + + #[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"); + } +} diff --git a/src/tui.rs b/src/tui.rs index 203ea65..de5b8bc 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -7,11 +7,16 @@ macro_rules! warning { use ansi_term::Colour::Red; println!("{} {}", Red.bold().paint("[!]"), $name); }; - // quiet mode - ($name:expr, $quiet:expr) => { + ($name:expr, $greppable:expr, $accessible:expr) => { use ansi_term::Colour::Red; - if !$quiet { - println!("{} {}", Red.bold().paint("[!]"), $name); + // if not greppable then print, otherwise no else statement so do not print. + if !$greppable { + if $accessible { + // Don't print the ascii art + println!("{}", $name); + } else { + println!("{} {}", Red.bold().paint("[!]"), $name); + } } }; } @@ -22,26 +27,38 @@ macro_rules! detail { use ansi_term::Colour::Blue; println!("{} {}", Blue.bold().paint("[~]"), $name); }; - ($name:expr, $quiet:expr) => { + ($name:expr, $greppable:expr, $accessible:expr) => { use ansi_term::Colour::Blue; - if !$quiet { - println!("{} {}", Blue.bold().paint("[~]"), $name); + // if not greppable then print, otherwise no else statement so do not print. + if !$greppable { + if $accessible { + // Don't print the ascii art + println!("{}", $name); + } else { + println!("{} {}", Blue.bold().paint("[~]"), $name); + } } }; } #[macro_export] macro_rules! output { - ($name:expr, $quiet:expr) => { - use ansi_term::Colour::RGB; - if !$quiet { - println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name); - } - }; ($name:expr) => { use ansi_term::Colour::RGB; println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name); }; + ($name:expr, $greppable:expr, $accessible:expr) => { + use ansi_term::Colour::RGB; + // if not greppable then print, otherwise no else statement so do not print. + if !$greppable { + if $accessible { + // Don't print the ascii art + println!("{}", $name); + } else { + println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name); + } + } + }; } #[macro_export] @@ -55,6 +72,7 @@ macro_rules! funny_opening { "Real hackers hack time ⌛", "Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan", "😵 https://admin.tryhackme.com", + "0day was here ♥", ]; let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap(); diff --git a/tests/timelimits.rs b/tests/timelimits.rs new file mode 100644 index 0000000..0692e73 --- /dev/null +++ b/tests/timelimits.rs @@ -0,0 +1,109 @@ +/* + * 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), + ); + } +}