├── .SRCINFO ├── .all-contributorsrc ├── .dockerignore ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── config.yml ├── release-drafter.yml └── workflows │ ├── action.yml │ ├── basic.yml │ └── markdown.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── config.toml ├── contributing.Dockerfile ├── contributing.md ├── fixtures ├── .rustscan_scripts │ ├── test_script.pl │ ├── test_script.py │ ├── test_script.sh │ ├── test_script.txt │ └── test_script_invalid_headers.txt ├── empty_hosts.txt ├── hosts.txt ├── naughty_strings.txt └── test_rustscan_scripts.toml ├── pictures ├── 8seconds.gif ├── accessible.gif ├── accessible.yml ├── adaptive.gif ├── apple.png ├── arch.png ├── debiian.jpg ├── docker.png ├── fast.gif ├── fast.yml ├── intro.gif ├── kali.png ├── newfast.yml ├── nice.yml ├── render1595455985190.gif ├── render1595457244085.gif ├── render1595457288918.gif ├── rust.png ├── rustscan.png ├── scripts.gif ├── with_rustscan.gif └── without_rustscan.gif ├── rustscan-debbuilder ├── Dockerfile ├── entrypoint.sh └── run.sh ├── src ├── benchmark │ └── mod.rs ├── input.rs ├── main.rs ├── port_strategy │ ├── mod.rs │ └── range_iterator.rs ├── scanner │ ├── mod.rs │ └── socket_iterator.rs ├── scripts │ └── mod.rs └── tui.rs └── tests └── timelimits.rs /.SRCINFO: -------------------------------------------------------------------------------- 1 | pkgbase = rustscan-bin 2 | pkgdesc = Faster Nmap Scanning with Rust 3 | pkgver = 1.1.0 4 | pkgrel = 1 5 | url = https://github.com/rustscan/rustscan 6 | arch = x86_64 7 | license = MIT 8 | depends = rustup 9 | provides = rustscan 10 | 11 | pkgname = rustscan-bin 12 | 13 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "brandonskerritt", 10 | "name": "Brandon", 11 | "avatar_url": "https://avatars3.githubusercontent.com/u/10378052?v=4", 12 | "profile": "https://skerritt.blog", 13 | "contributions": [ 14 | "infra", 15 | "test", 16 | "code", 17 | "design" 18 | ] 19 | }, 20 | { 21 | "login": "SakiiR", 22 | "name": "SakiiR", 23 | "avatar_url": "https://avatars1.githubusercontent.com/u/9950578?v=4", 24 | "profile": "https://sakiir.ovh", 25 | "contributions": [ 26 | "code", 27 | "bug" 28 | ] 29 | }, 30 | { 31 | "login": "smackhack", 32 | "name": "smackhack", 33 | "avatar_url": "https://avatars2.githubusercontent.com/u/48143394?v=4", 34 | "profile": "https://github.com/smackhack", 35 | "contributions": [ 36 | "ideas", 37 | "example" 38 | ] 39 | }, 40 | { 41 | "login": "bernardoamc", 42 | "name": "Bernardo Araujo", 43 | "avatar_url": "https://avatars0.githubusercontent.com/u/428984?v=4", 44 | "profile": "http://bernardoamc.github.io/", 45 | "contributions": [ 46 | "code", 47 | "bug", 48 | "design" 49 | ] 50 | }, 51 | { 52 | "login": "Isona", 53 | "name": "Izzy Whistlecroft", 54 | "avatar_url": "https://avatars2.githubusercontent.com/u/11759523?v=4", 55 | "profile": "https://github.com/Isona", 56 | "contributions": [ 57 | "bug" 58 | ] 59 | }, 60 | { 61 | "login": "imlonghao", 62 | "name": "imlonghao", 63 | "avatar_url": "https://avatars1.githubusercontent.com/u/4951333?v=4", 64 | "profile": "https://imlonghao.com", 65 | "contributions": [ 66 | "bug", 67 | "maintenance" 68 | ] 69 | }, 70 | { 71 | "login": "royharoush", 72 | "name": "royharoush", 73 | "avatar_url": "https://avatars3.githubusercontent.com/u/8113056?v=4", 74 | "profile": "https://github.com/royharoush", 75 | "contributions": [ 76 | "ideas", 77 | "design" 78 | ] 79 | }, 80 | { 81 | "login": "Atul9", 82 | "name": "Atul Bhosale", 83 | "avatar_url": "https://avatars1.githubusercontent.com/u/3390330?v=4", 84 | "profile": "https://github.com/Atul9", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "TGotwig", 91 | "name": "Thomas Gotwig", 92 | "avatar_url": "https://avatars0.githubusercontent.com/u/30773779?v=4", 93 | "profile": "https://tgotwig.me", 94 | "contributions": [ 95 | "platform" 96 | ] 97 | }, 98 | { 99 | "login": "remigourdon", 100 | "name": "Rémi Gourdon", 101 | "avatar_url": "https://avatars3.githubusercontent.com/u/2874133?v=4", 102 | "profile": "https://github.com/remigourdon", 103 | "contributions": [ 104 | "doc", 105 | "code" 106 | ] 107 | }, 108 | { 109 | "login": "cmnatic", 110 | "name": "Ben (CMNatic)", 111 | "avatar_url": "https://avatars3.githubusercontent.com/u/4163116?v=4", 112 | "profile": "https://cmnatic.co.uk", 113 | "contributions": [ 114 | "code", 115 | "doc", 116 | "design" 117 | ] 118 | }, 119 | { 120 | "login": "Ferryistaken", 121 | "name": "Alessandro Ferrari", 122 | "avatar_url": "https://avatars3.githubusercontent.com/u/47927670?v=4", 123 | "profile": "https://github.com/Ferryistaken", 124 | "contributions": [ 125 | "content" 126 | ] 127 | }, 128 | { 129 | "login": "Phenomite", 130 | "name": "Phenomite", 131 | "avatar_url": "https://avatars2.githubusercontent.com/u/8285537?v=4", 132 | "profile": "https://github.com/Phenomite", 133 | "contributions": [ 134 | "content" 135 | ] 136 | }, 137 | { 138 | "login": "SuperSandro2000", 139 | "name": "Sandro", 140 | "avatar_url": "https://avatars2.githubusercontent.com/u/7258858?v=4", 141 | "profile": "https://supersandro.de/", 142 | "contributions": [ 143 | "content", 144 | "bug", 145 | "code" 146 | ] 147 | }, 148 | { 149 | "login": "caass", 150 | "name": "Cass", 151 | "avatar_url": "https://avatars2.githubusercontent.com/u/25358963?v=4", 152 | "profile": "https://swag.lgbt", 153 | "contributions": [ 154 | "platform", 155 | "code", 156 | "bug" 157 | ] 158 | }, 159 | { 160 | "login": "niklasmohrin", 161 | "name": "Niklas Mohrin", 162 | "avatar_url": "https://avatars0.githubusercontent.com/u/47574893?v=4", 163 | "profile": "https://github.com/niklasmohrin", 164 | "contributions": [ 165 | "doc", 166 | "code", 167 | "bug" 168 | ] 169 | }, 170 | { 171 | "login": "tim77", 172 | "name": "Artem Polishchuk", 173 | "avatar_url": "https://avatars0.githubusercontent.com/u/5614476?v=4", 174 | "profile": "https://liberapay.com/Artem4/", 175 | "contributions": [ 176 | "platform" 177 | ] 178 | }, 179 | { 180 | "login": "buermarc", 181 | "name": "buermarc", 182 | "avatar_url": "https://avatars2.githubusercontent.com/u/44375277?v=4", 183 | "profile": "https://github.com/buermarc", 184 | "contributions": [ 185 | "code" 186 | ] 187 | }, 188 | { 189 | "login": "bergabman", 190 | "name": "bergabman", 191 | "avatar_url": "https://avatars1.githubusercontent.com/u/44554109?v=4", 192 | "profile": "https://github.com/bergabman", 193 | "contributions": [ 194 | "code", 195 | "bug", 196 | "design" 197 | ] 198 | }, 199 | { 200 | "login": "dmitris", 201 | "name": "Dmitry Savintsev", 202 | "avatar_url": "https://avatars0.githubusercontent.com/u/31205?v=4", 203 | "profile": "https://github.com/dmitris", 204 | "contributions": [ 205 | "code" 206 | ] 207 | }, 208 | { 209 | "login": "bofh69", 210 | "name": "Sebastian Andersson", 211 | "avatar_url": "https://avatars3.githubusercontent.com/u/1444315?v=4", 212 | "profile": "https://github.com/bofh69", 213 | "contributions": [ 214 | "code" 215 | ] 216 | }, 217 | { 218 | "login": "mattcorbin", 219 | "name": "Matt Corbin", 220 | "avatar_url": "https://avatars3.githubusercontent.com/u/6537765?v=4", 221 | "profile": "https://github.com/mattcorbin", 222 | "contributions": [ 223 | "code" 224 | ] 225 | }, 226 | { 227 | "login": "rootsploit", 228 | "name": "RootSploit", 229 | "avatar_url": "https://avatars2.githubusercontent.com/u/67270834?v=4", 230 | "profile": "http://rootsploit.com", 231 | "contributions": [ 232 | "blog" 233 | ] 234 | }, 235 | { 236 | "login": "eiffel-fl", 237 | "name": "eiffel-fl", 238 | "avatar_url": "https://avatars2.githubusercontent.com/u/12171754?v=4", 239 | "profile": "https://github.com/eiffel-fl", 240 | "contributions": [ 241 | "code" 242 | ] 243 | }, 244 | { 245 | "login": "u5surf", 246 | "name": "Y.Horie", 247 | "avatar_url": "https://avatars1.githubusercontent.com/u/14180225?v=4", 248 | "profile": "https://github.com/u5surf", 249 | "contributions": [ 250 | "code" 251 | ] 252 | }, 253 | { 254 | "login": "okrplay", 255 | "name": "Oskar", 256 | "avatar_url": "https://avatars3.githubusercontent.com/u/32576280?v=4", 257 | "profile": "https://github.com/okrplay", 258 | "contributions": [ 259 | "code", 260 | "test" 261 | ] 262 | } 263 | ], 264 | "contributorsPerLine": 7, 265 | "projectName": "RustScan", 266 | "projectOwner": "RustScan", 267 | "repoType": "github", 268 | "repoHost": "https://github.com", 269 | "skipCi": true 270 | } 271 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | Dockerfile 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bee-san 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | keyword: "// TODO" 3 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | template: | 2 | ## What’s Changed 3 | 4 | $CHANGES 5 | -------------------------------------------------------------------------------- /.github/workflows/action.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | 3 | on: push 4 | 5 | jobs: 6 | markdown-link-check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@master 10 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 11 | -------------------------------------------------------------------------------- /.github/workflows/basic.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | override: true 16 | - uses: actions-rs/cargo@v1 17 | with: 18 | command: check 19 | 20 | test: 21 | name: Test Suite 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | override: true 30 | - uses: actions-rs/cargo@v1 31 | with: 32 | command: test 33 | 34 | fmt: 35 | name: Rustfmt 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions-rs/toolchain@v1 40 | with: 41 | profile: minimal 42 | toolchain: stable 43 | override: true 44 | - run: rustup component add rustfmt 45 | - uses: actions-rs/cargo@v1 46 | with: 47 | command: fmt 48 | args: --all -- --check 49 | 50 | clippy: 51 | name: Clippy linting 52 | runs-on: ubuntu-latest 53 | steps: 54 | - uses: actions/checkout@v2 55 | - uses: actions-rs/toolchain@v1 56 | with: 57 | toolchain: stable 58 | components: clippy 59 | override: true 60 | - uses: actions-rs/clippy-check@v1 61 | with: 62 | token: ${{ secrets.GITHUB_TOKEN }} 63 | args: --all-features 64 | 65 | links: 66 | name: markdown-link-check 67 | runs-on: ubuntu-latest 68 | steps: 69 | - uses: actions/checkout@master 70 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/markdown.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | schedule: 8 | # Run everyday at 9:00 AM (See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07) 9 | - cron: "0 9 * * *" 10 | 11 | jobs: 12 | markdown-link-check: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@master 16 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 17 | with: 18 | use-quiet-mode: 'yes' 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | 11 | 12 | #Added by cargo 13 | 14 | /target 15 | 16 | # Created by https://www.toptal.com/developers/gitignore/api/rust,archlinuxpackages 17 | # Edit at https://www.toptal.com/developers/gitignore?templates=rust,archlinuxpackages 18 | 19 | ### ArchLinuxPackages ### 20 | *.tar 21 | *.tar.* 22 | *.jar 23 | *.exe 24 | *.msi 25 | *.zip 26 | *.tgz 27 | *.log 28 | *.log.* 29 | *.sig 30 | 31 | pkg/ 32 | 33 | ### Rust ### 34 | # Generated by Cargo 35 | # will have compiled files and executables 36 | /target/ 37 | 38 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 39 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 40 | 41 | # These are backup files generated by rustfmt 42 | **/*.rs.bk 43 | 44 | # End of https://www.toptal.com/developers/gitignore/api/rust,archlinuxpackages 45 | *.deb 46 | 47 | 48 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 49 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 50 | 51 | # User-specific stuff 52 | .idea/**/workspace.xml 53 | .idea/**/tasks.xml 54 | .idea/**/usage.statistics.xml 55 | .idea/**/dictionaries 56 | .idea/**/shelf 57 | 58 | # Generated files 59 | .idea/**/contentModel.xml 60 | 61 | # Sensitive or high-churn files 62 | .idea/**/dataSources/ 63 | .idea/**/dataSources.ids 64 | .idea/**/dataSources.local.xml 65 | .idea/**/sqlDataSources.xml 66 | .idea/**/dynamic.xml 67 | .idea/**/uiDesigner.xml 68 | .idea/**/dbnavigator.xml 69 | 70 | # Gradle 71 | .idea/**/gradle.xml 72 | .idea/**/libraries 73 | 74 | # Gradle and Maven with auto-import 75 | # When using Gradle or Maven with auto-import, you should exclude module files, 76 | # since they will be recreated, and may cause churn. Uncomment if using 77 | # auto-import. 78 | # .idea/artifacts 79 | # .idea/compiler.xml 80 | # .idea/jarRepositories.xml 81 | # .idea/modules.xml 82 | # .idea/*.iml 83 | # .idea/modules 84 | # *.iml 85 | # *.ipr 86 | 87 | # CMake 88 | cmake-build-*/ 89 | 90 | # Mongo Explorer plugin 91 | .idea/**/mongoSettings.xml 92 | 93 | # File-based project format 94 | *.iws 95 | 96 | # IntelliJ 97 | out/ 98 | 99 | # mpeltonen/sbt-idea plugin 100 | .idea_modules/ 101 | 102 | # JIRA plugin 103 | atlassian-ide-plugin.xml 104 | 105 | # Cursive Clojure plugin 106 | .idea/replstate.xml 107 | 108 | # Crashlytics plugin (for Android Studio and IntelliJ) 109 | com_crashlytics_export_strings.xml 110 | crashlytics.properties 111 | crashlytics-build.properties 112 | fabric.properties 113 | 114 | # Editor-based Rest Client 115 | .idea/httpRequests 116 | 117 | # Android studio 3.1+ serialized cache file 118 | .idea/caches/build_file_checksums.ser 119 | 120 | .idea/* 121 | .idea 122 | .idea* 123 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/rustscan_scripting_engine"] 2 | path = src/rustscan_scripting_engine 3 | url = https://github.com/RustScan/rustscan_scripting_engine 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | os: 3 | - linux 4 | - osx 5 | arch: 6 | - amd64 7 | - arm64 8 | services: 9 | - docker 10 | before_install: 11 | - rustup component add rustfmt 12 | rust: 13 | - stable 14 | - beta 15 | jobs: 16 | fast_finish: true 17 | script: 18 | - cargo build 19 | - cargo test 20 | - "ulimit -n 5000; cargo test timelimits:: -- --ignored --test-threads=1 --show-output" 21 | - cargo fmt -- --check 22 | after_failure: 23 | - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh 24 | - chmod +x send.sh 25 | - "./send.sh failure $WEBHOOK_URL" 26 | after_success: | 27 | if [ -n "${GITHUB_TOKEN}" ]; then 28 | [ "${TRAVIS_BRANCH} " = master ] && 29 | [ "${TRAVIS_PULL_REQUEST}" = false ] && 30 | cargo doc && 31 | echo "" > target/doc/index.html && 32 | sudo pip install ghp-import && 33 | ghp-import -n target/doc && 34 | git push -fq https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 35 | fi 36 | env: 37 | global: 38 | secure: Gf24LG7Hr3Q0drWyPrvISTL35Z57DxdS/wIOo7v+ZdauzY3Qjvv3Lh4TY//C4sdN0g1CLj4yjg7LQOqF4XRdh2Qah7JZPv/i2B5uz9nwjTsoRzmmMWML9VtfT1u53nSFqsNeXbvpmoNG9rfMXcoiyd+Lv+EgniHphDEBlXvS9n9KMcpOi1F2x7bsH0EK37wQYKMTpCCd5TPUPdZou7BJNiZS3ikRLpTRFF7uIN4gYtRH9kU5uBSphWgnJ6eBm2ItzZcUPOgrjAoF4RkmZAzGG4idDh1Qkf3No3nZwT3U5NSk8h7owtJ4oADlRgYqYKCB4YcC9cAKCJgDsyMlKKMkEyagvw1seFnBLCY3ljsW4EklpVEZ0rVqZOx4pTkIaxIxytLTC9pHmdDSnhdhFknOcVAQQWjg+nAZfD07QUeAqUdr0i/vxQoZitvbNY0znzEarycE1waKvwlkiB4gt385U1CBfYldydnOnRKkdtLNrCLClu13HU0djmSXMhojQJhTuNxTHtZAf5fKcWIWwkVbNZ5MNKYjQh4rKufqW3o1iHphB8dfAGsDEPv0RQMtVbu6H4+clCouLkFlxHOIrrOJ5SXQ8cIATPhnJR7y4cSDdRaN0DRLRjeLAIKlbOGBtNuFC+BftocaTY5qN42aKAMqQcT30ycfiLuRVJNiQJ5pkto= 39 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at our discord via a private message to any of the owners. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the Contributor Covenant homepage, version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | homepage: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustscan" 3 | version = "2.0.0" 4 | authors = ["Bee "] 5 | edition = "2018" 6 | description = "Faster Nmap Scanning with Rust" 7 | homepage = "https://github.com/rustscan/rustscan" 8 | repository = "https://github.com/rustscan/rustscan" 9 | license = "MIT" 10 | keywords = ["port", "scanning", "nmap"] 11 | categories = ["command-line-utilities"] 12 | readme="README.md" 13 | exclude = [ 14 | ".github/*", 15 | "pictures/*", 16 | "rustscan-debbuilder/*", 17 | ] 18 | 19 | [dependencies] 20 | colored = "2.0.0" 21 | structopt = "0.3.20" 22 | async-std = "1.7.0" 23 | futures = "0.3" 24 | rlimit = "0.5.4" 25 | shell-words = "1.0.0" 26 | log = "0.4.0" 27 | env_logger = "0.8.2" 28 | dirs = "3.0.1" 29 | gcd = "2.0.1" 30 | rand = "0.7.3" 31 | colorful = "0.2.1" 32 | ansi_term = "0.12.1" 33 | toml = "0.5.7" 34 | serde = "1.0.124" 35 | serde_derive = "1.0.116" 36 | cidr-utils = "0.5.1" 37 | itertools = "0.9.0" 38 | trust-dns-resolver = { version = "0.19.5", features = ["dns-over-rustls"] } 39 | anyhow = "1.0.40" 40 | subprocess = "0.2.6" 41 | text_placeholder = { version = "0.4", features = ["struct_context"] } 42 | 43 | [dev-dependencies] 44 | wait-timeout = "0.2" 45 | 46 | [package.metadata.deb] 47 | depends = "$auto, nmap" 48 | section = "rust" 49 | 50 | [profile.release] 51 | lto = true 52 | panic = 'abort' 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:alpine as builder 2 | LABEL maintainer="RustScan " 3 | RUN apk add --no-cache build-base 4 | 5 | # Encourage some layer caching here rather then copying entire directory that includes docs to builder container ~CMN 6 | WORKDIR /usr/src/rustscan 7 | COPY Cargo.toml Cargo.lock ./ 8 | COPY src/ src/ 9 | RUN cargo install --path . 10 | 11 | FROM alpine:3.12 12 | LABEL author="Hydragyrum " 13 | RUN addgroup -S rustscan && \ 14 | adduser -S -G rustscan rustscan && \ 15 | ulimit -n 100000 && \ 16 | apk add --no-cache nmap nmap-scripts wget 17 | COPY --from=builder /usr/local/cargo/bin/rustscan /usr/local/bin/rustscan 18 | USER rustscan 19 | ENTRYPOINT [ "/usr/local/bin/rustscan" ] 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | cargo install cross 3 | 4 | build: 5 | make build-linux 6 | make build-mac 7 | 8 | make shasum 9 | 10 | build-linux: 11 | @echo 'Building for Linux... 🐧' 12 | cross build --release --target=x86_64-unknown-linux-musl 13 | mkdir -p target/release-archives && tar -C target/x86_64-unknown-linux-musl/release -czf target/release-archives/rustscan-linux.tar.gz rustscan 14 | 15 | build-mac: 16 | @echo 'Building for MacOS... 🍏' 17 | cross build --release --target=x86_64-apple-darwin 18 | mkdir -p target/release-archives && tar -C target/x86_64-apple-darwin/release -czf target/release-archives/rustscan-mac.tar.gz rustscan 19 | 20 | shasum: 21 | shasum -a 256 target/release-archives/rustscan-*.tar.gz 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | ➡️ 3 | Discord | 4 | Installation Guide | 5 | Usage Guide 6 | ⬅️ 7 |
8 | 9 |

10 |

11 | The Modern Port Scanner.
Fast, smart, effective. 12 |

13 |

14 | AUR version 15 | 16 | GitHub All Releases 17 | Crates.io 18 | Discord 19 | Actions 20 |

21 |
22 | 23 | |

🐋 Docker (Recommended)

|

👩‍💻 Kali / Debian

|

🏗️ Arch

|

🔧 Homebrew

| 24 | | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | 25 | |

|

|

|

| 26 | | `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` | 27 | 28 |


29 | 30 | # 🤔 What is this? 31 | 32 | ![fast](pictures/fast.gif) 33 | 34 | The Modern Port Scanner. **Find ports quickly (3 seconds at its fastest)**. Run scripts through our scripting engine (Python, Lua, Shell supported). 35 | 36 | # ✨ Features 37 | 38 | - Scans all 65k ports in **3 seconds**. 39 | - Full scripting engine support. Automatically pipe results into Nmap, or use our scripts (or write your own) to do whatever you want. 40 | - Adaptive learning. RustScan improves the more you use it. No bloated machine learning here, just basic maths. 41 | - The usuals you would expect. IPv6, CIDR, file input and more. 42 | - Automatically pipes ports into Nmap. 43 | 44 | ## ‼️ Important Links 45 | 46 | | Installation Guide | Documentation | Discord | 47 | | -------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------- | 48 | | 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://rustscan.github.io/RustScan/) | 🦜 [Discord](http://discord.skerritt.blog) | 49 | 50 | ## 🙋 Table of Contents 51 | 52 | - 📖 [Installation Guide](https://github.com/RustScan/RustScan/wiki/Installation-Guide) 53 | - 🐋 [Docker Usage](https://github.com/RustScan/RustScan/wiki/Installation-Guide) 54 | - 🦜 [Discord](http://discord.skerritt.blog) 55 | - 🤸 [Usage](https://github.com/RustScan/RustScan/wiki/Usage) 56 | - 🎪 [Community](https://github.com/RustScan/RustScan#-community) 57 | 58 | # 🔭 Why RustScan? 59 | 60 | RustScan is a modern take on the port scanner. Sleek & fast. All while providing extensive extendability to you. 61 | 62 | Not to mention RustScan uses Adaptive Learning to improve itself over time, making it the best port scanner for **you**. 63 | 64 | ## 🧋 Speed 65 | 66 | ![fast](pictures/fast.gif) 67 | 68 | Speed is guaranteed via RustScan. However, if you want to run a slow scan due to stealth that is possible too. 69 | 70 | Firstly, let's talk code. 71 | 72 | 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. 73 | 74 | [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?". 75 | 76 | 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. 77 | 78 | [Read more here](https://github.com/RustScan/RustScan/wiki/Increasing-Speed-&-Accuracy). 79 | 80 | ## ⚙️ Extensible 81 | 82 | ![scripts](pictures/scripts.gif) 83 | 84 | _RustScan piping results into the custom Python script_ 85 | 86 | RustScan has a new scripting engine which allows anyone to write scripts in most languages. Python, Lua, Shell are all supported. 87 | 88 | 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. 89 | 90 | The possibilities are endless -- and you can write scripts in whatever language you feel comfortable with. 91 | 92 | [Read more here](https://github.com/RustScan/RustScan/wiki/RustScan-Scripting-Engine). 93 | 94 | ## 🌊 Adaptive 95 | 96 | ![adaptive](pictures/adaptive.gif) 97 | 98 | _RustScan automatically fine-tuning itself to match the host OS_. 99 | 100 | 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**. 101 | 102 | 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). 103 | 104 | ## 👩‍🦯 Accessible 105 | 106 | ![fast](pictures/accessible.gif) 107 | 108 | RustScan is one of the first penetration testing tools that aims to be entirely accessible. 109 | 110 | [Most penetration testing tools are not accessible](https://bees.substack.com/p/making-hacking-accessible), which negatively affects the whole industry. 111 | 112 | 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. 113 | 114 | # 📖 Full Installation Guide 115 | 116 | You can find our guide [here](https://github.com/RustScan/RustScan/wiki/Installation-Guide). 117 | 118 | ## 🦊 Community Distributions 119 | 120 | Here are all of RustScan's community distributions. 121 | 122 | 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. 123 | 124 | - [OpenSuse](https://software.opensuse.org/package/rustscan?search_term=rustscan) 125 | - [Fedora/CentOS](https://copr.fedorainfracloud.org/coprs/atim/rustscan/) 126 | 127 | [![Packaging status](https://repology.org/badge/vertical-allrepos/rustscan.svg)](https://repology.org/project/rustscan/versions) 128 | 129 | # 🤸 Usage 130 | 131 | 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). 132 | 133 | We also have documentation about our config file [here](https://github.com/RustScan/RustScan/wiki/Config-File). 134 | 135 | # 🎪 Community 136 | 137 | [Read this to learn how to contribute](https://github.com/RustScan/RustScan/wiki/Contributing). 138 | 139 | ## Contributors ✨ 140 | 141 | 142 | [![All Contributors](https://img.shields.io/badge/all_contributors-26-orange.svg?style=flat-square)](#contributors-) 143 | 144 | 145 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 |

Brandon

🚇 ⚠️ 💻 🎨

SakiiR

💻 🐛

smackhack

🤔 💡

Bernardo Araujo

💻 🐛 🎨

Izzy Whistlecroft

🐛

imlonghao

🐛 🚧

royharoush

🤔 🎨

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

💻 ⚠️
186 | 187 | 188 | 189 | 190 | 191 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 192 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | ip="127.0.0.1" 2 | 3 | # The hashmap of {ports: {1:1}} 4 | [ports] 5 | 1 = 1 6 | 3 = 1 7 | 4 = 1 8 | 6 = 1 9 | 7 = 1 10 | 9 = 1 11 | 13 = 1 12 | 17 = 1 13 | 19 = 1 14 | 20 = 1 15 | 21 = 1 16 | 22 = 1 17 | 23 = 1 18 | 24 = 1 19 | 25 = 1 20 | 26 = 1 21 | 30 = 1 22 | 32 = 1 23 | 33 = 1 24 | 37 = 1 25 | 42 = 1 26 | 43 = 1 27 | 49 = 1 28 | 53 = 1 29 | 70 = 1 30 | 79 = 1 31 | 80 = 1 32 | 81 = 1 33 | 82 = 1 34 | 83 = 1 35 | 84 = 1 36 | 85 = 1 37 | 88 = 1 38 | 89 = 1 39 | 90 = 1 40 | 99 = 1 41 | 100 = 1 42 | 106 = 1 43 | 109 = 1 44 | 110 = 1 45 | 111 = 1 46 | 113 = 1 47 | 119 = 1 48 | 125 = 1 49 | 135 = 1 50 | 139 = 1 51 | 143 = 1 52 | 144 = 1 53 | 146 = 1 54 | 161 = 1 55 | 163 = 1 56 | 179 = 1 57 | 199 = 1 58 | 211 = 1 59 | 212 = 1 60 | 222 = 1 61 | 254 = 1 62 | 255 = 1 63 | 256 = 1 64 | 259 = 1 65 | 264 = 1 66 | 280 = 1 67 | 301 = 1 68 | 306 = 1 69 | 311 = 1 70 | 340 = 1 71 | 366 = 1 72 | 389 = 1 73 | 406 = 1 74 | 407 = 1 75 | 416 = 1 76 | 417 = 1 77 | 425 = 1 78 | 427 = 1 79 | 443 = 1 80 | 444 = 1 81 | 445 = 1 82 | 458 = 1 83 | 464 = 1 84 | 465 = 1 85 | 481 = 1 86 | 497 = 1 87 | 500 = 1 88 | 512 = 1 89 | 513 = 1 90 | 514 = 1 91 | 515 = 1 92 | 524 = 1 93 | 541 = 1 94 | 543 = 1 95 | 544 = 1 96 | 545 = 1 97 | 548 = 1 98 | 554 = 1 99 | 555 = 1 100 | 563 = 1 101 | 587 = 1 102 | 593 = 1 103 | 616 = 1 104 | 617 = 1 105 | 625 = 1 106 | 631 = 1 107 | 636 = 1 108 | 646 = 1 109 | 648 = 1 110 | 666 = 1 111 | 667 = 1 112 | 668 = 1 113 | 683 = 1 114 | 687 = 1 115 | 691 = 1 116 | 700 = 1 117 | 705 = 1 118 | 711 = 1 119 | 714 = 1 120 | 720 = 1 121 | 722 = 1 122 | 726 = 1 123 | 749 = 1 124 | 765 = 1 125 | 777 = 1 126 | 783 = 1 127 | 787 = 1 128 | 800 = 1 129 | 801 = 1 130 | 808 = 1 131 | 843 = 1 132 | 873 = 1 133 | 880 = 1 134 | 888 = 1 135 | 898 = 1 136 | 900 = 1 137 | 901 = 1 138 | 902 = 1 139 | 903 = 1 140 | 911 = 1 141 | 912 = 1 142 | 981 = 1 143 | 987 = 1 144 | 990 = 1 145 | 992 = 1 146 | 993 = 1 147 | 995 = 1 148 | 999 = 1 149 | 1000 = 1 150 | 1001 = 1 151 | 1002 = 1 152 | 1007 = 1 153 | 1009 = 1 154 | 1010 = 1 155 | 1011 = 1 156 | 1021 = 1 157 | 1022 = 1 158 | 1023 = 1 159 | 1024 = 1 160 | 1025 = 1 161 | 1026 = 1 162 | 1027 = 1 163 | 1028 = 1 164 | 1029 = 1 165 | 1030 = 1 166 | 1031 = 1 167 | 1032 = 1 168 | 1033 = 1 169 | 1034 = 1 170 | 1035 = 1 171 | 1036 = 1 172 | 1037 = 1 173 | 1038 = 1 174 | 1039 = 1 175 | 1040 = 1 176 | 1041 = 1 177 | 1042 = 1 178 | 1043 = 1 179 | 1044 = 1 180 | 1045 = 1 181 | 1046 = 1 182 | 1047 = 1 183 | 1048 = 1 184 | 1049 = 1 185 | 1050 = 1 186 | 1051 = 1 187 | 1052 = 1 188 | 1053 = 1 189 | 1054 = 1 190 | 1055 = 1 191 | 1056 = 1 192 | 1057 = 1 193 | 1058 = 1 194 | 1059 = 1 195 | 1060 = 1 196 | 1061 = 1 197 | 1062 = 1 198 | 1063 = 1 199 | 1064 = 1 200 | 1065 = 1 201 | 1066 = 1 202 | 1067 = 1 203 | 1068 = 1 204 | 1069 = 1 205 | 1070 = 1 206 | 1071 = 1 207 | 1072 = 1 208 | 1073 = 1 209 | 1074 = 1 210 | 1075 = 1 211 | 1076 = 1 212 | 1077 = 1 213 | 1078 = 1 214 | 1079 = 1 215 | 1080 = 1 216 | 1081 = 1 217 | 1082 = 1 218 | 1083 = 1 219 | 1084 = 1 220 | 1085 = 1 221 | 1086 = 1 222 | 1087 = 1 223 | 1088 = 1 224 | 1089 = 1 225 | 1090 = 1 226 | 1091 = 1 227 | 1092 = 1 228 | 1093 = 1 229 | 1094 = 1 230 | 1095 = 1 231 | 1096 = 1 232 | 1097 = 1 233 | 1098 = 1 234 | 1099 = 1 235 | 1100 = 1 236 | 1102 = 1 237 | 1104 = 1 238 | 1105 = 1 239 | 1106 = 1 240 | 1107 = 1 241 | 1108 = 1 242 | 1110 = 1 243 | 1111 = 1 244 | 1112 = 1 245 | 1113 = 1 246 | 1114 = 1 247 | 1117 = 1 248 | 1119 = 1 249 | 1121 = 1 250 | 1122 = 1 251 | 1123 = 1 252 | 1124 = 1 253 | 1126 = 1 254 | 1130 = 1 255 | 1131 = 1 256 | 1132 = 1 257 | 1137 = 1 258 | 1138 = 1 259 | 1141 = 1 260 | 1145 = 1 261 | 1147 = 1 262 | 1148 = 1 263 | 1149 = 1 264 | 1151 = 1 265 | 1152 = 1 266 | 1154 = 1 267 | 1163 = 1 268 | 1164 = 1 269 | 1165 = 1 270 | 1166 = 1 271 | 1169 = 1 272 | 1174 = 1 273 | 1175 = 1 274 | 1183 = 1 275 | 1185 = 1 276 | 1186 = 1 277 | 1187 = 1 278 | 1192 = 1 279 | 1198 = 1 280 | 1199 = 1 281 | 1201 = 1 282 | 1213 = 1 283 | 1216 = 1 284 | 1217 = 1 285 | 1218 = 1 286 | 1233 = 1 287 | 1234 = 1 288 | 1236 = 1 289 | 1244 = 1 290 | 1247 = 1 291 | 1248 = 1 292 | 1259 = 1 293 | 1271 = 1 294 | 1272 = 1 295 | 1277 = 1 296 | 1287 = 1 297 | 1296 = 1 298 | 1300 = 1 299 | 1301 = 1 300 | 1309 = 1 301 | 1310 = 1 302 | 1311 = 1 303 | 1322 = 1 304 | 1328 = 1 305 | 1334 = 1 306 | 1352 = 1 307 | 1417 = 1 308 | 1433 = 1 309 | 1434 = 1 310 | 1443 = 1 311 | 1455 = 1 312 | 1461 = 1 313 | 1494 = 1 314 | 1500 = 1 315 | 1501 = 1 316 | 1503 = 1 317 | 1521 = 1 318 | 1524 = 1 319 | 1533 = 1 320 | 1556 = 1 321 | 1580 = 1 322 | 1583 = 1 323 | 1594 = 1 324 | 1600 = 1 325 | 1641 = 1 326 | 1658 = 1 327 | 1666 = 1 328 | 1687 = 1 329 | 1688 = 1 330 | 1700 = 1 331 | 1717 = 1 332 | 1718 = 1 333 | 1719 = 1 334 | 1720 = 1 335 | 1721 = 1 336 | 1723 = 1 337 | 1755 = 1 338 | 1761 = 1 339 | 1782 = 1 340 | 1783 = 1 341 | 1801 = 1 342 | 1805 = 1 343 | 1812 = 1 344 | 1839 = 1 345 | 1840 = 1 346 | 1862 = 1 347 | 1863 = 1 348 | 1864 = 1 349 | 1875 = 1 350 | 1900 = 1 351 | 1914 = 1 352 | 1935 = 1 353 | 1947 = 1 354 | 1971 = 1 355 | 1972 = 1 356 | 1974 = 1 357 | 1984 = 1 358 | 1998 = 1 359 | 1999 = 1 360 | 2000 = 1 361 | 2001 = 1 362 | 2002 = 1 363 | 2003 = 1 364 | 2004 = 1 365 | 2005 = 1 366 | 2006 = 1 367 | 2007 = 1 368 | 2008 = 1 369 | 2009 = 1 370 | 2010 = 1 371 | 2013 = 1 372 | 2020 = 1 373 | 2021 = 1 374 | 2022 = 1 375 | 2030 = 1 376 | 2033 = 1 377 | 2034 = 1 378 | 2035 = 1 379 | 2038 = 1 380 | 2040 = 1 381 | 2041 = 1 382 | 2042 = 1 383 | 2043 = 1 384 | 2045 = 1 385 | 2046 = 1 386 | 2047 = 1 387 | 2048 = 1 388 | 2049 = 1 389 | 2065 = 1 390 | 2068 = 1 391 | 2099 = 1 392 | 2100 = 1 393 | 2103 = 1 394 | 2105 = 1 395 | 2106 = 1 396 | 2107 = 1 397 | 2111 = 1 398 | 2119 = 1 399 | 2121 = 1 400 | 2126 = 1 401 | 2135 = 1 402 | 2144 = 1 403 | 2160 = 1 404 | 2161 = 1 405 | 2170 = 1 406 | 2179 = 1 407 | 2190 = 1 408 | 2191 = 1 409 | 2196 = 1 410 | 2200 = 1 411 | 2222 = 1 412 | 2251 = 1 413 | 2260 = 1 414 | 2288 = 1 415 | 2301 = 1 416 | 2323 = 1 417 | 2366 = 1 418 | 2381 = 1 419 | 2382 = 1 420 | 2383 = 1 421 | 2393 = 1 422 | 2394 = 1 423 | 2399 = 1 424 | 2401 = 1 425 | 2492 = 1 426 | 2500 = 1 427 | 2522 = 1 428 | 2525 = 1 429 | 2557 = 1 430 | 2601 = 1 431 | 2602 = 1 432 | 2604 = 1 433 | 2605 = 1 434 | 2607 = 1 435 | 2608 = 1 436 | 2638 = 1 437 | 2701 = 1 438 | 2702 = 1 439 | 2710 = 1 440 | 2717 = 1 441 | 2718 = 1 442 | 2725 = 1 443 | 2800 = 1 444 | 2809 = 1 445 | 2811 = 1 446 | 2869 = 1 447 | 2875 = 1 448 | 2909 = 1 449 | 2910 = 1 450 | 2920 = 1 451 | 2967 = 1 452 | 2968 = 1 453 | 2998 = 1 454 | 3000 = 1 455 | 3001 = 1 456 | 3003 = 1 457 | 3005 = 1 458 | 3006 = 1 459 | 3007 = 1 460 | 3011 = 1 461 | 3013 = 1 462 | 3017 = 1 463 | 3030 = 1 464 | 3031 = 1 465 | 3052 = 1 466 | 3071 = 1 467 | 3077 = 1 468 | 3128 = 1 469 | 3168 = 1 470 | 3211 = 1 471 | 3221 = 1 472 | 3260 = 1 473 | 3261 = 1 474 | 3268 = 1 475 | 3269 = 1 476 | 3283 = 1 477 | 3300 = 1 478 | 3301 = 1 479 | 3306 = 1 480 | 3322 = 1 481 | 3323 = 1 482 | 3324 = 1 483 | 3325 = 1 484 | 3333 = 1 485 | 3351 = 1 486 | 3367 = 1 487 | 3369 = 1 488 | 3370 = 1 489 | 3371 = 1 490 | 3372 = 1 491 | 3389 = 1 492 | 3390 = 1 493 | 3404 = 1 494 | 3476 = 1 495 | 3493 = 1 496 | 3517 = 1 497 | 3527 = 1 498 | 3546 = 1 499 | 3551 = 1 500 | 3580 = 1 501 | 3659 = 1 502 | 3689 = 1 503 | 3690 = 1 504 | 3703 = 1 505 | 3737 = 1 506 | 3766 = 1 507 | 3784 = 1 508 | 3800 = 1 509 | 3801 = 1 510 | 3809 = 1 511 | 3814 = 1 512 | 3826 = 1 513 | 3827 = 1 514 | 3828 = 1 515 | 3851 = 1 516 | 3869 = 1 517 | 3871 = 1 518 | 3878 = 1 519 | 3880 = 1 520 | 3889 = 1 521 | 3905 = 1 522 | 3914 = 1 523 | 3918 = 1 524 | 3920 = 1 525 | 3945 = 1 526 | 3971 = 1 527 | 3986 = 1 528 | 3995 = 1 529 | 3998 = 1 530 | 4000 = 1 531 | 4001 = 1 532 | 4002 = 1 533 | 4003 = 1 534 | 4004 = 1 535 | 4005 = 1 536 | 4006 = 1 537 | 4045 = 1 538 | 4111 = 1 539 | 4125 = 1 540 | 4126 = 1 541 | 4129 = 1 542 | 4224 = 1 543 | 4242 = 1 544 | 4279 = 1 545 | 4321 = 1 546 | 4343 = 1 547 | 4443 = 1 548 | 4444 = 1 549 | 4445 = 1 550 | 4446 = 1 551 | 4449 = 1 552 | 4550 = 1 553 | 4567 = 1 554 | 4662 = 1 555 | 4848 = 1 556 | 4899 = 1 557 | 4900 = 1 558 | 4998 = 1 559 | 5000 = 1 560 | 5001 = 1 561 | 5002 = 1 562 | 5003 = 1 563 | 5004 = 1 564 | 5009 = 1 565 | 5030 = 1 566 | 5033 = 1 567 | 5050 = 1 568 | 5051 = 1 569 | 5054 = 1 570 | 5060 = 1 571 | 5061 = 1 572 | 5080 = 1 573 | 5087 = 1 574 | 5100 = 1 575 | 5101 = 1 576 | 5102 = 1 577 | 5120 = 1 578 | 5190 = 1 579 | 5200 = 1 580 | 5214 = 1 581 | 5221 = 1 582 | 5222 = 1 583 | 5225 = 1 584 | 5226 = 1 585 | 5269 = 1 586 | 5280 = 1 587 | 5298 = 1 588 | 5357 = 1 589 | 5405 = 1 590 | 5414 = 1 591 | 5431 = 1 592 | 5432 = 1 593 | 5440 = 1 594 | 5500 = 1 595 | 5510 = 1 596 | 5544 = 1 597 | 5550 = 1 598 | 5555 = 1 599 | 5560 = 1 600 | 5566 = 1 601 | 5631 = 1 602 | 5633 = 1 603 | 5666 = 1 604 | 5678 = 1 605 | 5679 = 1 606 | 5718 = 1 607 | 5730 = 1 608 | 5800 = 1 609 | 5801 = 1 610 | 5802 = 1 611 | 5810 = 1 612 | 5811 = 1 613 | 5815 = 1 614 | 5822 = 1 615 | 5825 = 1 616 | 5850 = 1 617 | 5859 = 1 618 | 5862 = 1 619 | 5877 = 1 620 | 5900 = 1 621 | 5901 = 1 622 | 5902 = 1 623 | 5903 = 1 624 | 5904 = 1 625 | 5906 = 1 626 | 5907 = 1 627 | 5910 = 1 628 | 5911 = 1 629 | 5915 = 1 630 | 5922 = 1 631 | 5925 = 1 632 | 5950 = 1 633 | 5952 = 1 634 | 5959 = 1 635 | 5960 = 1 636 | 5961 = 1 637 | 5962 = 1 638 | 5963 = 1 639 | 5987 = 1 640 | 5988 = 1 641 | 5989 = 1 642 | 5998 = 1 643 | 5999 = 1 644 | 6000 = 1 645 | 6001 = 1 646 | 6002 = 1 647 | 6003 = 1 648 | 6004 = 1 649 | 6005 = 1 650 | 6006 = 1 651 | 6007 = 1 652 | 6009 = 1 653 | 6025 = 1 654 | 6059 = 1 655 | 6100 = 1 656 | 6101 = 1 657 | 6106 = 1 658 | 6112 = 1 659 | 6123 = 1 660 | 6129 = 1 661 | 6156 = 1 662 | 6346 = 1 663 | 6389 = 1 664 | 6502 = 1 665 | 6510 = 1 666 | 6543 = 1 667 | 6547 = 1 668 | 6565 = 1 669 | 6566 = 1 670 | 6567 = 1 671 | 6580 = 1 672 | 6646 = 1 673 | 6666 = 1 674 | 6667 = 1 675 | 6668 = 1 676 | 6669 = 1 677 | 6689 = 1 678 | 6692 = 1 679 | 6699 = 1 680 | 6779 = 1 681 | 6788 = 1 682 | 6789 = 1 683 | 6792 = 1 684 | 6839 = 1 685 | 6881 = 1 686 | 6901 = 1 687 | 6969 = 1 688 | 7000 = 1 689 | 7001 = 1 690 | 7002 = 1 691 | 7004 = 1 692 | 7007 = 1 693 | 7019 = 1 694 | 7025 = 1 695 | 7070 = 1 696 | 7100 = 1 697 | 7103 = 1 698 | 7106 = 1 699 | 7200 = 1 700 | 7201 = 1 701 | 7402 = 1 702 | 7435 = 1 703 | 7443 = 1 704 | 7496 = 1 705 | 7512 = 1 706 | 7625 = 1 707 | 7627 = 1 708 | 7676 = 1 709 | 7741 = 1 710 | 7777 = 1 711 | 7778 = 1 712 | 7800 = 1 713 | 7911 = 1 714 | 7920 = 1 715 | 7921 = 1 716 | 7937 = 1 717 | 7938 = 1 718 | 7999 = 1 719 | 8000 = 1 720 | 8001 = 1 721 | 8002 = 1 722 | 8007 = 1 723 | 8008 = 1 724 | 8009 = 1 725 | 8010 = 1 726 | 8011 = 1 727 | 8021 = 1 728 | 8022 = 1 729 | 8031 = 1 730 | 8042 = 1 731 | 8045 = 1 732 | 8080 = 1 733 | 8081 = 1 734 | 8082 = 1 735 | 8083 = 1 736 | 8084 = 1 737 | 8085 = 1 738 | 8086 = 1 739 | 8087 = 1 740 | 8088 = 1 741 | 8089 = 1 742 | 8090 = 1 743 | 8093 = 1 744 | 8099 = 1 745 | 8100 = 1 746 | 8180 = 1 747 | 8181 = 1 748 | 8192 = 1 749 | 8193 = 1 750 | 8194 = 1 751 | 8200 = 1 752 | 8222 = 1 753 | 8254 = 1 754 | 8290 = 1 755 | 8291 = 1 756 | 8292 = 1 757 | 8300 = 1 758 | 8333 = 1 759 | 8383 = 1 760 | 8400 = 1 761 | 8402 = 1 762 | 8443 = 1 763 | 8500 = 1 764 | 8600 = 1 765 | 8649 = 1 766 | 8651 = 1 767 | 8652 = 1 768 | 8654 = 1 769 | 8701 = 1 770 | 8800 = 1 771 | 8873 = 1 772 | 8888 = 1 773 | 8899 = 1 774 | 8994 = 1 775 | 9000 = 1 776 | 9001 = 1 777 | 9002 = 1 778 | 9003 = 1 779 | 9009 = 1 780 | 9010 = 1 781 | 9011 = 1 782 | 9040 = 1 783 | 9050 = 1 784 | 9071 = 1 785 | 9080 = 1 786 | 9081 = 1 787 | 9090 = 1 788 | 9091 = 1 789 | 9099 = 1 790 | 9100 = 1 791 | 9101 = 1 792 | 9102 = 1 793 | 9103 = 1 794 | 9110 = 1 795 | 9111 = 1 796 | 9200 = 1 797 | 9207 = 1 798 | 9220 = 1 799 | 9290 = 1 800 | 9415 = 1 801 | 9418 = 1 802 | 9485 = 1 803 | 9500 = 1 804 | 9502 = 1 805 | 9503 = 1 806 | 9535 = 1 807 | 9575 = 1 808 | 9593 = 1 809 | 9594 = 1 810 | 9595 = 1 811 | 9618 = 1 812 | 9666 = 1 813 | 9876 = 1 814 | 9877 = 1 815 | 9878 = 1 816 | 9898 = 1 817 | 9900 = 1 818 | 9917 = 1 819 | 9929 = 1 820 | 9943 = 1 821 | 9944 = 1 822 | 9968 = 1 823 | 9998 = 1 824 | 9999 = 1 825 | 10000 = 1 826 | 10001 = 1 827 | 10002 = 1 828 | 10003 = 1 829 | 10004 = 1 830 | 10009 = 1 831 | 10010 = 1 832 | 10012 = 1 833 | 10024 = 1 834 | 10025 = 1 835 | 10082 = 1 836 | 10180 = 1 837 | 10215 = 1 838 | 10243 = 1 839 | 10566 = 1 840 | 10616 = 1 841 | 10617 = 1 842 | 10621 = 1 843 | 10626 = 1 844 | 10628 = 1 845 | 10629 = 1 846 | 10778 = 1 847 | 11110 = 1 848 | 11111 = 1 849 | 11967 = 1 850 | 12000 = 1 851 | 12174 = 1 852 | 12265 = 1 853 | 12345 = 1 854 | 13456 = 1 855 | 13722 = 1 856 | 13782 = 1 857 | 13783 = 1 858 | 14000 = 1 859 | 14238 = 1 860 | 14441 = 1 861 | 14442 = 1 862 | 15000 = 1 863 | 15002 = 1 864 | 15003 = 1 865 | 15004 = 1 866 | 15660 = 1 867 | 15742 = 1 868 | 16000 = 1 869 | 16001 = 1 870 | 16012 = 1 871 | 16016 = 1 872 | 16018 = 1 873 | 16080 = 1 874 | 16113 = 1 875 | 16992 = 1 876 | 16993 = 1 877 | 17877 = 1 878 | 17988 = 1 879 | 18040 = 1 880 | 18101 = 1 881 | 18988 = 1 882 | 19101 = 1 883 | 19283 = 1 884 | 19315 = 1 885 | 19350 = 1 886 | 19780 = 1 887 | 19801 = 1 888 | 19842 = 1 889 | 20000 = 1 890 | 20005 = 1 891 | 20031 = 1 892 | 20221 = 1 893 | 20222 = 1 894 | 20828 = 1 895 | 21571 = 1 896 | 22939 = 1 897 | 23502 = 1 898 | 24444 = 1 899 | 24800 = 1 900 | 25734 = 1 901 | 25735 = 1 902 | 26214 = 1 903 | 27000 = 1 904 | 27352 = 1 905 | 27353 = 1 906 | 27355 = 1 907 | 27356 = 1 908 | 27715 = 1 909 | 28201 = 1 910 | 30000 = 1 911 | 30718 = 1 912 | 30951 = 1 913 | 31038 = 1 914 | 31337 = 1 915 | 32768 = 1 916 | 32769 = 1 917 | 32770 = 1 918 | 32771 = 1 919 | 32772 = 1 920 | 32773 = 1 921 | 32774 = 1 922 | 32775 = 1 923 | 32776 = 1 924 | 32777 = 1 925 | 32778 = 1 926 | 32779 = 1 927 | 32780 = 1 928 | 32781 = 1 929 | 32782 = 1 930 | 32783 = 1 931 | 32784 = 1 932 | 32785 = 1 933 | 33354 = 1 934 | 33899 = 1 935 | 34571 = 1 936 | 34572 = 1 937 | 34573 = 1 938 | 35500 = 1 939 | 38292 = 1 940 | 40193 = 1 941 | 40911 = 1 942 | 41511 = 1 943 | 42510 = 1 944 | 44176 = 1 945 | 44442 = 1 946 | 44443 = 1 947 | 44501 = 1 948 | 45100 = 1 949 | 48080 = 1 950 | 49152 = 1 951 | 49153 = 1 952 | 49154 = 1 953 | 49155 = 1 954 | 49156 = 1 955 | 49157 = 1 956 | 49158 = 1 957 | 49159 = 1 958 | 49160 = 1 959 | 49161 = 1 960 | 49163 = 1 961 | 49165 = 1 962 | 49167 = 1 963 | 49175 = 1 964 | 49176 = 1 965 | 49400 = 1 966 | 49999 = 1 967 | 50000 = 1 968 | 50001 = 1 969 | 50002 = 1 970 | 50003 = 1 971 | 50006 = 1 972 | 50300 = 1 973 | 50389 = 1 974 | 50500 = 1 975 | 50636 = 1 976 | 50800 = 1 977 | 51103 = 1 978 | 51493 = 1 979 | 52673 = 1 980 | 52822 = 1 981 | 52848 = 1 982 | 52869 = 1 983 | 54045 = 1 984 | 54328 = 1 985 | 55055 = 1 986 | 55056 = 1 987 | 55555 = 1 988 | 55600 = 1 989 | 56737 = 1 990 | 56738 = 1 991 | 57294 = 1 992 | 57797 = 1 993 | 58080 = 1 994 | 60020 = 1 995 | 60443 = 1 996 | 61532 = 1 997 | 61900 = 1 998 | 62078 = 1 999 | 63331 = 1 1000 | 64623 = 1 1001 | 64680 = 1 1002 | 65000 = 1 1003 | 65129 = 1 1004 | 65389 = 1 1005 | -------------------------------------------------------------------------------- /contributing.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust 2 | # Install nmap first. 3 | RUN apt-get update -qy && apt-get install -qy nmap 4 | # Then install rustfmt and clippy for cargo. 5 | RUN rustup component add rustfmt clippy -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | Howdy Space Cow-Person 🤠🌌 2 | 3 | RustScan is always looking for contributors. Whether that's spelling mistakes or major changes, your help is **wanted** and welcomed here. 4 | 5 | Before contributing, read our [code of conduct](https://github.com/RustScan/RustScan/blob/master/CODE_OF_CONDUCT.md). 6 | 7 | TL;DR if you abuse members of our community you will be **perma-banned** with no chance to get unbanned. No warnings either. 🤗 8 | 9 | RustScan has 2 major labels for GitHub issues you should look at: 10 | * Good First issue 11 | These are issues for newcomers to open source! 12 | [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) 13 | * Help wanted 14 | These are issues that aren't really for newcomers, but we could still do with help! 15 | [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) 16 | 17 | If you want to, solve the issue or comment on the issue for help. 18 | 19 | The [flow for contributing](https://guides.github.com/introduction/flow/) to open source software is: 20 | * Fork the repo 21 | * Make changes 22 | * Pull request to the repo 23 | 24 | And then comment on the issue that you've done. 25 | 26 | 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. 27 | 28 | If you have any feature suggestions or bugs, leave a GitHub issue. We welcome any and all support :D 29 | 30 | ## Rewarding you 31 | I cannot pay you :-( But, I can place your GitHub profile on the README under `#Contributors` as a thank you! :) 32 | 33 | ## Contributing development environment 34 | 35 | To ease contribution to RustScan, you can use the `contributing.Dockerfile` to create a Docker image ready to build and play with RustScan. 36 | To build it you just need to run: 37 | 38 | ```bash 39 | you@home:~/RustScan$ docker build -t rustscan_contributing -f contributing.Dockerfile 40 | ``` 41 | 42 | Then you need to run the container with a volume so it can access, *with read and write permissions*, to RustScan files: 43 | 44 | ```bash 45 | you@home:~/RustScan$ docker run -ti --rm -v "$PWD":/rustscan -w /rustscan rustscan_contributing bash 46 | ``` 47 | 48 | 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: 49 | 50 | ```bash 51 | root@container:/rustscan# cargo build 52 | ``` 53 | 54 | You are now ready to use RustScan: 55 | 56 | ```bash 57 | root@container:/rustscan# cargo run -- -b 2000 -t 5000 -a 127.0.0.1 58 | ``` 59 | 60 | You can also format, lint with `clippy` and test the code with the following commands: 61 | 62 | ```bash 63 | root@container:/rustscan# cargo fmt 64 | root@container:/rustscan# cargo clippy 65 | root@container:/rustscan# cargo test 66 | ``` -------------------------------------------------------------------------------- /fixtures/.rustscan_scripts/test_script.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | #tags = ["core_approved", "example",] 3 | #developer = [ "example", "https://example.org" ] 4 | #ports_separator = "," 5 | #call_format = "perl {{script}} {{ip}} {{port}}" 6 | 7 | # Sriptfile parser stops at the first blank line with parsing. 8 | # This script will run itself as an argument with the system installed perl interpreter, ports will be concatenated with "," . 9 | # Unused field: trigger_port = "80" 10 | # get total arg passed to this script 11 | my $total = $#ARGV + 1; 12 | my $counter = 1; 13 | 14 | # get script name 15 | my $scriptname = $0; 16 | 17 | print "Total args passed to $scriptname : $total\n"; 18 | 19 | # Use loop to print all args stored in an array called @ARGV 20 | foreach my $a(@ARGV) { 21 | print "Arg # $counter : $a\n"; 22 | $counter++; 23 | } -------------------------------------------------------------------------------- /fixtures/.rustscan_scripts/test_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | #tags = ["core_approved", "example",] 3 | #developer = [ "example", "https://example.org" ] 4 | #trigger_port = "80" 5 | #call_format = "python3 {{script}} {{ip}} {{port}}" 6 | 7 | # Sriptfile parser stops at the first blank line with parsing. 8 | # This script will run itself as an argument with the system installed python interpreter, only scanning port 80. 9 | # Unused filed: ports_separator = "," 10 | 11 | import sys 12 | 13 | print('Python script ran with arguments', str(sys.argv)) -------------------------------------------------------------------------------- /fixtures/.rustscan_scripts/test_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #tags = ["core_approved", "example",] 3 | #developer = [ "example", "https://example.org" ] 4 | #ports_separator = "," 5 | #call_format = "bash {{script}} {{ip}} {{port}}" 6 | 7 | # Sriptfile parser stops at the first blank line with parsing. 8 | # This script will run itself as an argument with the system installed bash interpreter, scanning all ports concatenated with "," . 9 | # Unused filed: trigger_port = "80" 10 | 11 | # print all arguments passed to the script 12 | echo $@ -------------------------------------------------------------------------------- /fixtures/.rustscan_scripts/test_script.txt: -------------------------------------------------------------------------------- 1 | #!intentional_blank_line 2 | #tags = ["core_approved", "example"] 3 | #developer = [ "example", "https://example.org" ] 4 | #ports_separator = "," 5 | #call_format = "nmap -vvv -p {{port}} {{ip}}" 6 | 7 | # Scriptfile parser stops at the first blank line with parsing. 8 | # This script will run the system installed nmap, ports will be concatenated with "," . 9 | # Unused field: trigger_port = "80" -------------------------------------------------------------------------------- /fixtures/.rustscan_scripts/test_script_invalid_headers.txt: -------------------------------------------------------------------------------- 1 | #!intentional_blank_line 2 | #tags = "core_approved" 3 | #developer = [ "example", "https://example.org" ] 4 | #ports_separator = "," 5 | #call_format = "nmap -vvv -p {{port}} {{ip}}" 6 | 7 | # tags has to be an array, thus this file won't be parsed -------------------------------------------------------------------------------- /fixtures/empty_hosts.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/fixtures/empty_hosts.txt -------------------------------------------------------------------------------- /fixtures/hosts.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | google.com 3 | example.com 4 | 66666666666666.666666666666666666.66666666666666666.6666666666 5 | .... 6 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.radiatorFixtures -------------------------------------------------------------------------------- /fixtures/naughty_strings.txt: -------------------------------------------------------------------------------- 1 | # https://github.com/minimaxir/big-list-of-naughty-strings 2 | 3 | 4 | ­؀؁؂؃؄؅؜۝܏᠎​‌‍‎‏‪‫‬‭‮⁠⁡⁢⁣⁤⁦⁧⁨⁩𑂽𛲠𛲡𛲢𛲣𝅳𝅴𝅵𝅶𝅷𝅸𝅹𝅺󠀁󠀠󠀡󠀢󠀣󠀤󠀥󠀦󠀧󠀨󠀩󠀪󠀫󠀬󠀭󠀮󠀯󠀰󠀱󠀲󠀳󠀴󠀵󠀶󠀷󠀸󠀹󠀺󠀻󠀼󠀽󠀾󠀿󠁀󠁁󠁂󠁃󠁄󠁅󠁆󠁇󠁈󠁉󠁊󠁋󠁌󠁍󠁎󠁏󠁐󠁑󠁒󠁓󠁔󠁕󠁖󠁗󠁘󠁙󠁚󠁛󠁜󠁝󠁞󠁟󠁠󠁡󠁢󠁣󠁤󠁥󠁦󠁧󠁨󠁩󠁪󠁫󠁬󠁭󠁮󠁯󠁰󠁱󠁲󠁳󠁴󠁵󠁶󠁷󠁸󠁹󠁺󠁻󠁼󠁽󠁾󠁿 5 | 6 | Ω≈ç√∫˜µ≤≥÷ 7 | åß∂ƒ©˙∆˚¬…æ 8 | œ∑´®†¥¨ˆøπ“‘ 9 | ¡™£¢∞§¶•ªº–≠ 10 | ¸˛Ç◊ı˜Â¯˘¿ 11 | ÅÍÎÏ˝ÓÔÒÚÆ☃ 12 | Œ„´‰ˇÁ¨ˆØ∏”’ 13 | `⁄€‹›fifl‡°·‚—± 14 | ⅛⅜⅝⅞ 15 | ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя 16 | ٠١٢٣٤٥٦٧٨٩ 17 | 18 | 19 | 20 | ⁰⁴⁵ 21 | ₀₁₂ 22 | ⁰⁴⁵₀₁₂ 23 | ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ 24 | 25 | 田中さんにあげて下さい 26 | パーティーへ行かないか 27 | 和製漢語 28 | 部落格 29 | 사회과학원 어학연구소 30 | 찦차를 타고 온 펲시맨과 쑛다리 똠방각하 31 | 社會科學院語學研究所 32 | 울란바토르 33 | 𠜎𠜱𠝹𠱓𠱸𠲖𠳏 34 | 𝐓𝐡𝐞 𝐪𝐮𝐢𝐜𝐤 𝐛𝐫𝐨𝐰𝐧 𝐟𝐨𝐱 𝐣𝐮𝐦𝐩𝐬 𝐨𝐯𝐞𝐫 𝐭𝐡𝐞 𝐥𝐚𝐳𝐲 𝐝𝐨𝐠 35 | 𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌 36 | 𝑻𝒉𝒆 𝒒𝒖𝒊𝒄𝒌 𝒃𝒓𝒐𝒘𝒏 𝒇𝒐𝒙 𝒋𝒖𝒎𝒑𝒔 𝒐𝒗𝒆𝒓 𝒕𝒉𝒆 𝒍𝒂𝒛𝒚 𝒅𝒐𝒈 37 | 𝓣𝓱𝓮 𝓺𝓾𝓲𝓬𝓴 𝓫𝓻𝓸𝔀𝓷 𝓯𝓸𝔁 𝓳𝓾𝓶𝓹𝓼 𝓸𝓿𝓮𝓻 𝓽𝓱𝓮 𝓵𝓪𝔃𝔂 𝓭𝓸𝓰 38 | 𝕋𝕙𝕖 𝕢𝕦𝕚𝕔𝕜 𝕓𝕣𝕠𝕨𝕟 𝕗𝕠𝕩 𝕛𝕦𝕞𝕡𝕤 𝕠𝕧𝕖𝕣 𝕥𝕙𝕖 𝕝𝕒𝕫𝕪 𝕕𝕠𝕘 39 | 𝚃𝚑𝚎 𝚚𝚞𝚒𝚌𝚔 𝚋𝚛𝚘𝚠𝚗 𝚏𝚘𝚡 𝚓𝚞𝚖𝚙𝚜 𝚘𝚟𝚎𝚛 𝚝𝚑𝚎 𝚕𝚊𝚣𝚢 𝚍𝚘𝚐 40 | ⒯⒣⒠ ⒬⒰⒤⒞⒦ ⒝⒭⒪⒲⒩ ⒡⒪⒳ ⒥⒰⒨⒫⒮ ⒪⒱⒠⒭ ⒯⒣⒠ ⒧⒜⒵⒴ ⒟⒪⒢ 41 | 42 | ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو. -------------------------------------------------------------------------------- /fixtures/test_rustscan_scripts.toml: -------------------------------------------------------------------------------- 1 | # Test/Example ScriptConfig file 2 | 3 | # Tags to filter on scripts. Only scripts containing all these tags will run. 4 | tags = ["core_approved", "example"] 5 | 6 | # If it's present then only those scripts will run which has a tag ports = "80". Not yet implemented. 7 | # 8 | # ex.: 9 | # ports = ["80"] 10 | # ports = ["80","81","8080"] 11 | ports = ["80"] 12 | 13 | # Only this developer(s) scripts to run. Not yet implemented. 14 | developer = ["example"] -------------------------------------------------------------------------------- /pictures/8seconds.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/8seconds.gif -------------------------------------------------------------------------------- /pictures/accessible.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/accessible.gif -------------------------------------------------------------------------------- /pictures/accessible.yml: -------------------------------------------------------------------------------- 1 | # The configurations that used for the recording, feel free to edit them 2 | config: 3 | 4 | # Specify a command to be executed 5 | # like `/bin/bash -l`, `ls`, or any other commands 6 | # the default is bash for Linux 7 | # or powershell.exe for Windows 8 | command: zsh 9 | 10 | # Specify the current working directory path 11 | # the default is the current working directory path 12 | cwd: /home/bee/Documents/RustScan 13 | 14 | # Export additional ENV variables 15 | env: 16 | recording: true 17 | 18 | # Explicitly set the number of columns 19 | # or use `auto` to take the current 20 | # number of columns of your shell 21 | cols: 107 22 | 23 | # Explicitly set the number of rows 24 | # or use `auto` to take the current 25 | # number of rows of your shell 26 | rows: 43 27 | 28 | # Amount of times to repeat GIF 29 | # If value is -1, play once 30 | # If value is 0, loop indefinitely 31 | # If value is a positive number, loop n times 32 | repeat: 0 33 | 34 | # Quality 35 | # 1 - 100 36 | quality: 100 37 | 38 | # Delay between frames in ms 39 | # If the value is `auto` use the actual recording delays 40 | frameDelay: auto 41 | 42 | # Maximum delay between frames in ms 43 | # Ignored if the `frameDelay` isn't set to `auto` 44 | # Set to `auto` to prevent limiting the max idle time 45 | maxIdleTime: 2000 46 | 47 | # The surrounding frame box 48 | # The `type` can be null, window, floating, or solid` 49 | # To hide the title use the value null 50 | # Don't forget to add a backgroundColor style with a null as type 51 | frameBox: 52 | type: floating 53 | title: Terminalizer 54 | style: 55 | border: 0px black solid 56 | # boxShadow: none 57 | # margin: 0px 58 | 59 | # Add a watermark image to the rendered gif 60 | # You need to specify an absolute path for 61 | # the image on your machine or a URL, and you can also 62 | # add your own CSS styles 63 | watermark: 64 | imagePath: null 65 | style: 66 | position: absolute 67 | right: 15px 68 | bottom: 15px 69 | width: 100px 70 | opacity: 0.9 71 | 72 | # Cursor style can be one of 73 | # `block`, `underline`, or `bar` 74 | cursorStyle: block 75 | 76 | # Font family 77 | # You can use any font that is installed on your machine 78 | # in CSS-like syntax 79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace" 80 | 81 | # The size of the font 82 | fontSize: 12 83 | 84 | # The height of lines 85 | lineHeight: 1 86 | 87 | # The spacing between letters 88 | letterSpacing: 0 89 | 90 | # Theme 91 | theme: 92 | background: "transparent" 93 | foreground: "#afafaf" 94 | cursor: "#c7c7c7" 95 | black: "#232628" 96 | red: "#fc4384" 97 | green: "#b3e33b" 98 | yellow: "#ffa727" 99 | blue: "#75dff2" 100 | magenta: "#ae89fe" 101 | cyan: "#708387" 102 | white: "#d5d5d0" 103 | brightBlack: "#626566" 104 | brightRed: "#ff7fac" 105 | brightGreen: "#c8ed71" 106 | brightYellow: "#ebdf86" 107 | brightBlue: "#75dff2" 108 | brightMagenta: "#ae89fe" 109 | brightCyan: "#b1c6ca" 110 | brightWhite: "#f9f9f4" 111 | 112 | # Records, feel free to edit them 113 | records: 114 | - delay: 436 115 | 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" 116 | - delay: 162 117 | 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" 118 | - delay: 471 119 | 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" 120 | - delay: 269 121 | 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" 122 | - delay: 1536 123 | content: "Open 127.0.0.1:38602\r\n" 124 | - delay: 395 125 | content: "Open 127.0.0.1:50078\r\n" 126 | - delay: 450 127 | 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" 128 | - delay: 143 129 | 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" 130 | - delay: 563 131 | content: "\e[?2004l\r\r\n" 132 | -------------------------------------------------------------------------------- /pictures/adaptive.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/adaptive.gif -------------------------------------------------------------------------------- /pictures/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/apple.png -------------------------------------------------------------------------------- /pictures/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/arch.png -------------------------------------------------------------------------------- /pictures/debiian.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/debiian.jpg -------------------------------------------------------------------------------- /pictures/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/docker.png -------------------------------------------------------------------------------- /pictures/fast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/fast.gif -------------------------------------------------------------------------------- /pictures/fast.yml: -------------------------------------------------------------------------------- 1 | # The configurations that used for the recording, feel free to edit them 2 | config: 3 | 4 | # Specify a command to be executed 5 | # like `/bin/bash -l`, `ls`, or any other commands 6 | # the default is bash for Linux 7 | # or powershell.exe for Windows 8 | command: zsh 9 | 10 | # Specify the current working directory path 11 | # the default is the current working directory path 12 | cwd: /home/bee/Documents/RustScan 13 | 14 | # Export additional ENV variables 15 | env: 16 | recording: true 17 | 18 | # Explicitly set the number of columns 19 | # or use `auto` to take the current 20 | # number of columns of your shell 21 | cols: 105 22 | 23 | # Explicitly set the number of rows 24 | # or use `auto` to take the current 25 | # number of rows of your shell 26 | rows: 34 27 | 28 | # Amount of times to repeat GIF 29 | # If value is -1, play once 30 | # If value is 0, loop indefinitely 31 | # If value is a positive number, loop n times 32 | repeat: 0 33 | 34 | # Quality 35 | # 1 - 100 36 | quality: 100 37 | 38 | # Delay between frames in ms 39 | # If the value is `auto` use the actual recording delays 40 | frameDelay: auto 41 | 42 | # Maximum delay between frames in ms 43 | # Ignored if the `frameDelay` isn't set to `auto` 44 | # Set to `auto` to prevent limiting the max idle time 45 | maxIdleTime: auto 46 | 47 | # The surrounding frame box 48 | # The `type` can be null, window, floating, or solid` 49 | # To hide the title use the value null 50 | # Don't forget to add a backgroundColor style with a null as type 51 | frameBox: 52 | type: floating 53 | title: Terminalizer 54 | style: 55 | border: 0px black solid 56 | # boxShadow: none 57 | # margin: 0px 58 | 59 | # Add a watermark image to the rendered gif 60 | # You need to specify an absolute path for 61 | # the image on your machine or a URL, and you can also 62 | # add your own CSS styles 63 | watermark: 64 | imagePath: null 65 | style: 66 | position: absolute 67 | right: 15px 68 | bottom: 15px 69 | width: 100px 70 | opacity: 0.9 71 | 72 | # Cursor style can be one of 73 | # `block`, `underline`, or `bar` 74 | cursorStyle: block 75 | 76 | # Font family 77 | # You can use any font that is installed on your machine 78 | # in CSS-like syntax 79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace" 80 | 81 | # The size of the font 82 | fontSize: 12 83 | 84 | # The height of lines 85 | lineHeight: 1 86 | 87 | # The spacing between letters 88 | letterSpacing: 0 89 | 90 | # Theme 91 | theme: 92 | background: "transparent" 93 | foreground: "#afafaf" 94 | cursor: "#c7c7c7" 95 | black: "#232628" 96 | red: "#fc4384" 97 | green: "#b3e33b" 98 | yellow: "#ffa727" 99 | blue: "#75dff2" 100 | magenta: "#ae89fe" 101 | cyan: "#708387" 102 | white: "#d5d5d0" 103 | brightBlack: "#626566" 104 | brightRed: "#ff7fac" 105 | brightGreen: "#c8ed71" 106 | brightYellow: "#ebdf86" 107 | brightBlue: "#75dff2" 108 | brightMagenta: "#ae89fe" 109 | brightCyan: "#b1c6ca" 110 | brightWhite: "#f9f9f4" 111 | 112 | # Records, feel free to edit them 113 | records: 114 | - delay: 815 115 | content: "\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;..ents/RustScan\a" 116 | - delay: 178 117 | 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 asyncMaster\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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.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" 118 | - delay: 406 119 | content: "\e[32mc\e[39m\e[90margo build --release && ./target/release/rustscan 127.0.0.1\e[39m\e[59D" 120 | - delay: 137 121 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m" 122 | - delay: 72 123 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m" 124 | - delay: 203 125 | content: "\b\b\b\e[0m\e[32mc\e[0m\e[32ma\e[0m\e[32mr\e[32mg\e[32mo\e[39m\e[39m \e[39mb\e[39mu\e[39mi\e[39ml\e[39md\e[39m \e[39m-\e[39m-\e[39mr\e[39me\e[39ml\e[39me\e[39ma\e[39ms\e[39me\e[39m \e[39m&\e[39m&\e[39m \e[32m.\e[32m/\e[32mt\e[32ma\e[32mr\e[32mg\e[32me\e[32mt\e[32m/\e[32mr\e[32me\e[32ml\e[32me\e[32ma\e[32ms\e[32me\e[32m/\e[32mr\e[32mu\e[32ms\e[32mt\e[32ms\e[32mc\e[32ma\e[32mn\e[39m\e[39m \e[39m1\e[39m2\e[39m7\e[39m.\e[39m0\e[39m.\e[39m0\e[39m.\e[39m1" 126 | - delay: 145 127 | content: "\e[?1l\e>" 128 | - delay: 7 129 | content: "\e[?2004l\r\r\n\e]2;cargo\a" 130 | - delay: 37 131 | content: "\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 87/92: futures-executor, smol \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 89/92: futures, async-std \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 90/92: async-std \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:193:13\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m192\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Unable to convert to socket address\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m----------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m193\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(io::Error::new(io::ErrorKind::Other, e.to_string()))\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unreachable_code)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:57:14\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m57\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Some(x) => {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_variables)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: 2 warnings emitted\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[32m Finished\e[0m release [optimized] target(s) in 0.03s\r\n\e[32m\r\n _____ _ _____ \r\n | __ \\ | | / ____| \r\n | |__) | _ ___| |_| (___ ___ __ _ _ __ \r\n | _ / | | / __| __|\\___ \\ / __/ _` | '_ \\ \r\n | | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |\r\n |_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|\r\n Faster nmap scanning with rust.\e[0m \r\n \e[31mAutomated Decryption Tool - https://github.com/ciphey/ciphey\e[0m \r\n \e[32mCreator https://github.com/brandonskerritt\e[0m\r\n" 132 | - delay: 326 133 | content: "Open \e[35m53\e[0m\r\nOpen \e[35m631\e[0m\r\n" 134 | - delay: 6 135 | content: "Open \e[35m1716\e[0m\r\n" 136 | - delay: 1138 137 | content: "Open \e[35m46624\e[0m\r\n" 138 | - delay: 156 139 | content: "Open \e[35m52880\e[0m\r\n" 140 | - delay: 146 141 | content: "\e[34mStarting nmap.\e[0m\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;..ents/RustScan\a" 142 | - delay: 9 143 | content: "Starting Nmap 7.80 ( https://nmap.org ) at 2020-07-22 22:12 BST\r\n" 144 | - delay: 153 145 | 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 asyncMaster\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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.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" 146 | - delay: 119 147 | content: "NSE: Loaded 151 scripts for scanning.\r\nNSE: Script Pre-scanning.\r\nNSE: Starting runlevel 1 (of 3) scan.\r\nInitiating NSE at 22:12\r\nCompleted NSE at 22:12, 0.00s elapsed\r\nNSE: Starting runlevel 2 (of 3) scan.\r\nInitiating NSE at 22:12\r\nCompleted NSE at 22:12, 0.00s elapsed\r\nNSE: Starting runlevel 3 (of 3) scan.\r\nInitiating NSE at 22:12\r\nCompleted NSE at 22:12, 0.00s elapsed\r\n" 148 | - delay: 21 149 | content: "Initiating Ping Scan at 22:12\r\nScanning 127.0.0.1 [2 ports]\r\nCompleted Ping Scan at 22:12, 0.00s elapsed (1 total hosts)\r\nInitiating Connect Scan at 22:12\r\nScanning localhost (127.0.0.1) [5 ports]\r\nDiscovered open port 53/tcp on 127.0.0.1\r\nDiscovered open port 46624/tcp on 127.0.0.1\r\nDiscovered open port 1716/tcp on 127.0.0.1\r\nDiscovered open port 631/tcp on 127.0.0.1\r\nCompleted Connect Scan at 22:12, 0.00s elapsed (5 total ports)\r\n" 150 | - delay: 52 151 | content: "Initiating Service scan at 22:12\r\nScanning 4 services on localhost (127.0.0.1)\r\n" 152 | - delay: 105 153 | content: "\e[?2004l\r\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;..ents/RustScan\a" 154 | - delay: 158 155 | 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 asyncMaster\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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[31m➜ \e[0m\e[31m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h" 156 | - delay: 1332 157 | content: "\e[?2004l\r\r\n" 158 | -------------------------------------------------------------------------------- /pictures/intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/intro.gif -------------------------------------------------------------------------------- /pictures/kali.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/kali.png -------------------------------------------------------------------------------- /pictures/newfast.yml: -------------------------------------------------------------------------------- 1 | # The configurations that used for the recording, feel free to edit them 2 | config: 3 | 4 | # Specify a command to be executed 5 | # like `/bin/bash -l`, `ls`, or any other commands 6 | # the default is bash for Linux 7 | # or powershell.exe for Windows 8 | command: zsh 9 | 10 | # Specify the current working directory path 11 | # the default is the current working directory path 12 | cwd: /home/bee/Documents/RustScan 13 | 14 | # Export additional ENV variables 15 | env: 16 | recording: true 17 | 18 | # Explicitly set the number of columns 19 | # or use `auto` to take the current 20 | # number of columns of your shell 21 | cols: 235 22 | 23 | # Explicitly set the number of rows 24 | # or use `auto` to take the current 25 | # number of rows of your shell 26 | rows: 31 27 | 28 | # Amount of times to repeat GIF 29 | # If value is -1, play once 30 | # If value is 0, loop indefinitely 31 | # If value is a positive number, loop n times 32 | repeat: 0 33 | 34 | # Quality 35 | # 1 - 100 36 | quality: 100 37 | 38 | # Delay between frames in ms 39 | # If the value is `auto` use the actual recording delays 40 | frameDelay: auto 41 | 42 | # Maximum delay between frames in ms 43 | # Ignored if the `frameDelay` isn't set to `auto` 44 | # Set to `auto` to prevent limiting the max idle time 45 | maxIdleTime: auto 46 | 47 | # The surrounding frame box 48 | # The `type` can be null, window, floating, or solid` 49 | # To hide the title use the value null 50 | # Don't forget to add a backgroundColor style with a null as type 51 | frameBox: 52 | type: floating 53 | title: Terminalizer 54 | style: 55 | border: 0px black solid 56 | # boxShadow: none 57 | # margin: 0px 58 | 59 | # Add a watermark image to the rendered gif 60 | # You need to specify an absolute path for 61 | # the image on your machine or a URL, and you can also 62 | # add your own CSS styles 63 | watermark: 64 | imagePath: null 65 | style: 66 | position: absolute 67 | right: 15px 68 | bottom: 15px 69 | width: 100px 70 | opacity: 0.9 71 | 72 | # Cursor style can be one of 73 | # `block`, `underline`, or `bar` 74 | cursorStyle: block 75 | 76 | # Font family 77 | # You can use any font that is installed on your machine 78 | # in CSS-like syntax 79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace" 80 | 81 | # The size of the font 82 | fontSize: 12 83 | 84 | # The height of lines 85 | lineHeight: 1 86 | 87 | # The spacing between letters 88 | letterSpacing: 0 89 | 90 | # Theme 91 | theme: 92 | background: "transparent" 93 | foreground: "#afafaf" 94 | cursor: "#c7c7c7" 95 | black: "#232628" 96 | red: "#fc4384" 97 | green: "#b3e33b" 98 | yellow: "#ffa727" 99 | blue: "#75dff2" 100 | magenta: "#ae89fe" 101 | cyan: "#708387" 102 | white: "#d5d5d0" 103 | brightBlack: "#626566" 104 | brightRed: "#ff7fac" 105 | brightGreen: "#c8ed71" 106 | brightYellow: "#ebdf86" 107 | brightBlue: "#75dff2" 108 | brightMagenta: "#ae89fe" 109 | brightCyan: "#b1c6ca" 110 | brightWhite: "#f9f9f4" 111 | 112 | # Records, feel free to edit them 113 | records: 114 | - delay: 818 115 | 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" 116 | - delay: 187 117 | 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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.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" 118 | - delay: 432 119 | content: "\e[32mc\e[39m\e[90margo build --release && ./target/release/rustscan 8.8.8.8\e[39m\e[57D" 120 | - delay: 106 121 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m" 122 | - delay: 115 123 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m" 124 | - delay: 178 125 | content: "\b\b\b\e[0m\e[32mc\e[0m\e[32ma\e[0m\e[32mr\e[32mg\e[32mo\e[39m\e[39m \e[39mb\e[39mu\e[39mi\e[39ml\e[39md\e[39m \e[39m-\e[39m-\e[39mr\e[39me\e[39ml\e[39me\e[39ma\e[39ms\e[39me\e[39m \e[39m&\e[39m&\e[39m \e[32m.\e[32m/\e[32mt\e[32ma\e[32mr\e[32mg\e[32me\e[32mt\e[32m/\e[32mr\e[32me\e[32ml\e[32me\e[32ma\e[32ms\e[32me\e[32m/\e[32mr\e[32mu\e[32ms\e[32mt\e[32ms\e[32mc\e[32ma\e[32mn\e[39m\e[39m \e[39m8\e[39m.\e[39m8\e[39m.\e[39m8\e[39m.\e[39m8" 126 | - delay: 143 127 | content: "\e[?1l\e>" 128 | - delay: 8 129 | content: "\e[?2004l\r\r\n\e]2;cargo build --release && ./target/release/rustscan 8.8.8.8\a\e]1;cargo\a" 130 | - delay: 40 131 | content: "\e[0m\e[0m\e[1m\e[36m Building\e[0m [=================================================> ] 82/92: proc-macro-nested(build), rayon-core \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==================================================> ] 83/92: rayon, proc-macro-nested(build) \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===================================================> ] 84/92: proc-macro-nested(build) \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===================================================> ] 85/92: proc-macro-nested \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 86/92: futures-util \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 87/92: smol, futures-executor \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 89/92: async-std, futures \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 90/92: async-std \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused import: `Command`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:5:26\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m5\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0muse std::process::{exit, Command};\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_imports)]` on by default\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:181:21\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m180\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Too many open files. Please reduce batch size.\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m---------------------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m181\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(e)\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unreachable_code)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:189:13\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m188\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Unable to convert to socket address\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m----------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m189\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(io::Error::new(io::ErrorKind::Other, e.to_string()))\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `command_run`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:55:9\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m55\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m let command_run: String = match command_matches {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_command_run`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_variables)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:57:14\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m57\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Some(x) => {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_x`\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: 5 warnings emitted\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[32m Finished\e[0m release [optimized] target(s) in 0.03s\r\n\e[32m\r\n _____ _ _____ \r\n | __ \\ | | / ____| \r\n | |__) | _ ___| |_| (___ ___ __ _ _ __ \r\n | _ / | | / __| __|\\___ \\ / __/ _` | '_ \\ \r\n | | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |\r\n |_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|\r\n Faster nmap scanning with rust.\e[0m \r\n \e[31mAutomated Decryption Tool - https://github.com/ciphey/ciphey\e[0m \r\n \e[32mCreator https://github.com/brandonskerritt\e[0m\r\n" 132 | - delay: 368 133 | content: "Open \e[35m53\e[0m\r\n" 134 | - delay: 37 135 | content: "Open \e[35m853\e[0m\r\n" 136 | - delay: 849 137 | content: "Open \e[35m443\e[0m\r\n" 138 | - delay: 6605 139 | content: "\e[34mStarting nmap.\e[0m\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" 140 | - delay: 175 141 | 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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mtook \e[0m\e[1m\e[33m8s\e[0m\e[33m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[31m➜ \e[0m\e[31m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h" 142 | - delay: 554 143 | content: "\e[?2004l\r\r\n" 144 | -------------------------------------------------------------------------------- /pictures/nice.yml: -------------------------------------------------------------------------------- 1 | # The configurations that used for the recording, feel free to edit them 2 | config: 3 | 4 | # Specify a command to be executed 5 | # like `/bin/bash -l`, `ls`, or any other commands 6 | # the default is bash for Linux 7 | # or powershell.exe for Windows 8 | command: zsh 9 | 10 | # Specify the current working directory path 11 | # the default is the current working directory path 12 | cwd: /home/bee/Documents/RustScan 13 | 14 | # Export additional ENV variables 15 | env: 16 | recording: true 17 | 18 | # Explicitly set the number of columns 19 | # or use `auto` to take the current 20 | # number of columns of your shell 21 | cols: 108 22 | 23 | # Explicitly set the number of rows 24 | # or use `auto` to take the current 25 | # number of rows of your shell 26 | rows: 45 27 | 28 | # Amount of times to repeat GIF 29 | # If value is -1, play once 30 | # If value is 0, loop indefinitely 31 | # If value is a positive number, loop n times 32 | repeat: 0 33 | 34 | # Quality 35 | # 1 - 100 36 | quality: 100 37 | 38 | # Delay between frames in ms 39 | # If the value is `auto` use the actual recording delays 40 | frameDelay: auto 41 | 42 | # Maximum delay between frames in ms 43 | # Ignored if the `frameDelay` isn't set to `auto` 44 | # Set to `auto` to prevent limiting the max idle time 45 | maxIdleTime: auto 46 | 47 | # The surrounding frame box 48 | # The `type` can be null, window, floating, or solid` 49 | # To hide the title use the value null 50 | # Don't forget to add a backgroundColor style with a null as type 51 | frameBox: 52 | type: floating 53 | title: Terminalizer 54 | style: 55 | border: 0px black solid 56 | # boxShadow: none 57 | # margin: 0px 58 | 59 | # Add a watermark image to the rendered gif 60 | # You need to specify an absolute path for 61 | # the image on your machine or a URL, and you can also 62 | # add your own CSS styles 63 | watermark: 64 | imagePath: null 65 | style: 66 | position: absolute 67 | right: 15px 68 | bottom: 15px 69 | width: 100px 70 | opacity: 0.9 71 | 72 | # Cursor style can be one of 73 | # `block`, `underline`, or `bar` 74 | cursorStyle: block 75 | 76 | # Font family 77 | # You can use any font that is installed on your machine 78 | # in CSS-like syntax 79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace" 80 | 81 | # The size of the font 82 | fontSize: 12 83 | 84 | # The height of lines 85 | lineHeight: 1 86 | 87 | # The spacing between letters 88 | letterSpacing: 0 89 | 90 | # Theme 91 | theme: 92 | background: "transparent" 93 | foreground: "#afafaf" 94 | cursor: "#c7c7c7" 95 | black: "#232628" 96 | red: "#fc4384" 97 | green: "#b3e33b" 98 | yellow: "#ffa727" 99 | blue: "#75dff2" 100 | magenta: "#ae89fe" 101 | cyan: "#708387" 102 | white: "#d5d5d0" 103 | brightBlack: "#626566" 104 | brightRed: "#ff7fac" 105 | brightGreen: "#c8ed71" 106 | brightYellow: "#ebdf86" 107 | brightBlue: "#75dff2" 108 | brightMagenta: "#ae89fe" 109 | brightCyan: "#b1c6ca" 110 | brightWhite: "#f9f9f4" 111 | 112 | # Records, feel free to edit them 113 | records: 114 | - delay: 895 115 | content: "No protocol specified\r\nCan't open display :0\r\n" 116 | - delay: 253 117 | 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" 118 | - delay: 29 119 | content: c 120 | - delay: 131 121 | content: a 122 | - delay: 29 123 | 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 asyncMaster\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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.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[?2004hc" 124 | - delay: 5 125 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m\e[90mrgo build --release && ./target/release/rustscan 127.0.0.1\e[39m\e[58D" 126 | - delay: 719 127 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m" 128 | - delay: 697 129 | content: "\b\b\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m\e[0m\e[90mr\b" 130 | - delay: 172 131 | content: "\b\b\e[0m\e[32mc\e[39m\e[0m\e[90md\e[90m \e[90mR\e[90mu\e[90ms\e[90mt\e[90mS\e[90mc\e[90ma\e[90mn\e[39m\e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[59D" 132 | - delay: 174 133 | content: "\b\e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[11D" 134 | - delay: 2955 135 | content: "\e[32mc\e[39m\e[90md RustScan\e[39m\e[10D" 136 | - delay: 111 137 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m\e[90mr\e[90mg\e[90mo\e[90m \e[90mb\e[90mu\e[90mi\e[90ml\e[90md\e[90m --release && ./target/release/rustscan 127.0.0.1\e[39m\e[58D" 138 | - delay: 99 139 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m" 140 | - delay: 192 141 | content: "\b\b\b\e[0m\e[32mc\e[0m\e[32ma\e[0m\e[32mr\e[32mg\e[32mo\e[39m\e[39m \e[39mb\e[39mu\e[39mi\e[39ml\e[39md\e[39m \e[39m-\e[39m-\e[39mr\e[39me\e[39ml\e[39me\e[39ma\e[39ms\e[39me\e[39m \e[39m&\e[39m&\e[39m \e[32m.\e[32m/\e[32mt\e[32ma\e[32mr\e[32mg\e[32me\e[32mt\e[32m/\e[32mr\e[32me\e[32ml\e[32me\e[32ma\e[32ms\e[32me\e[32m/\e[32mr\e[32mu\e[32ms\e[32mt\e[32ms\e[32mc\e[32ma\e[32mn\e[39m\e[39m \e[39m1\e[39m2\e[39m7\e[39m.\e[39m0\e[39m.\e[39m0\e[39m.\e[39m1" 142 | - delay: 217 143 | content: "\e[?1l\e[?2004l\r\r\n\e]2;cargo build --release && ./target/release/rustscan 127.0.0.1\a\e]1;cargo\a" 144 | - delay: 38 145 | content: "\e[0m\e[0m\e[1m\e[36m Building\e[0m [ ] 0/92: autocfg \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==========================================> ] 70/92: crossbeam-utils(build.r...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==========================================> ] 71/92: crossbeam-utils(build.r...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [============================================> ] 73/92: crossbeam-utils(build),...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=============================================> ] 75/92: crossbeam-utils, quote \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==============================================> ] 76/92: crossbeam-epoch, crossb...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===============================================> ] 78/92: crossbeam-deque, quote \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=================================================> ] 81/92: rayon, syn \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===================================================> ] 85/92: pin-project \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 86/92: futures-util \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=====================================================> ] 88/92: async-std, futures-exec...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 89/92: futures-executor \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 90/92: futures \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:193:13\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m192\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Unable to convert to socket address\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m----------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m193\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(io::Error::new(io::ErrorKind::Other, e.to_string()))\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unreachable_code)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:57:14\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m57\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Some(x) => {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_variables)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: 2 warnings emitted\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[32m Finished\e[0m release [optimized] target(s) in 0.03s\r\n\e[32m\r\n _____ _ _____ \r\n | __ \\ | | / ____| \r\n | |__) | _ ___| |_| (___ ___ __ _ _ __ \r\n | _ / | | / __| __|\\___ \\ / __/ _` | '_ \\ \r\n | | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |\r\n |_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|\r\n Faster nmap scanning with rust.\e[0m \r\n \e[31mAutomated Decryption Tool - https://github.com/ciphey/ciphey\e[0m \r\n \e[32mCreator https://github.com/brandonskerritt\e[0m\r\n" 146 | - delay: 309 147 | content: "Open \e[35m53\e[0m\r\nOpen \e[35m631\e[0m\r\n" 148 | - delay: 1132 149 | content: "Open \e[35m46624\e[0m\r\n" 150 | - delay: 136 151 | content: "Open \e[35m51934\e[0m\r\n" 152 | - delay: 159 153 | content: "\e[34mStarting nmap.\e[0m\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\aStarting Nmap 7.80 ( https://nmap.org ) at 2020-07-22 22:09 BST\r\n" 154 | - delay: 156 155 | 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 asyncMaster\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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.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[?2004h" 156 | - delay: 75 157 | content: "NSE: Loaded 151 scripts for scanning.\r\nNSE: Script Pre-scanning.\r\nNSE: Starting runlevel 1 (of 3) scan.\r\nInitiating NSE at 22:09\r\nCompleted NSE at 22:09, 0.00s elapsed\r\nNSE: Starting runlevel 2 (of 3) scan.\r\nInitiating NSE at 22:09\r\nCompleted NSE at 22:09, 0.00s elapsed\r\nNSE: Starting runlevel 3 (of 3) scan.\r\nInitiating NSE at 22:09\r\nCompleted NSE at 22:09, 0.00s elapsed\r\n" 158 | - delay: 21 159 | content: "Initiating Ping Scan at 22:09\r\nScanning 127.0.0.1 [2 ports]\r\nCompleted Ping Scan at 22:09, 0.00s elapsed (1 total hosts)\r\nInitiating Connect Scan at 22:09\r\nScanning localhost (127.0.0.1) [4 ports]\r\nDiscovered open port 53/tcp on 127.0.0.1\r\nDiscovered open port 631/tcp on 127.0.0.1\r\nDiscovered open port 46624/tcp on 127.0.0.1\r\nCompleted Connect Scan at 22:09, 0.00s elapsed (4 total ports)\r\n" 160 | - delay: 55 161 | content: "Initiating Service scan at 22:09\r\nScanning 3 services on localhost (127.0.0.1)\r\n" 162 | - delay: 42 163 | content: "\e[?2004l\r\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" 164 | - delay: 168 165 | 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 asyncMaster\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.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[31m➜ \e[0m\e[31m\e[39m\e[1m\e[0m\e[K\e[?1h\e[?2004h" 166 | - delay: 403 167 | content: "\e[?2004l\r\r\n" 168 | -------------------------------------------------------------------------------- /pictures/render1595455985190.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/render1595455985190.gif -------------------------------------------------------------------------------- /pictures/render1595457244085.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/render1595457244085.gif -------------------------------------------------------------------------------- /pictures/render1595457288918.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/render1595457288918.gif -------------------------------------------------------------------------------- /pictures/rust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/rust.png -------------------------------------------------------------------------------- /pictures/rustscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/rustscan.png -------------------------------------------------------------------------------- /pictures/scripts.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/scripts.gif -------------------------------------------------------------------------------- /pictures/with_rustscan.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/with_rustscan.gif -------------------------------------------------------------------------------- /pictures/without_rustscan.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/without_rustscan.gif -------------------------------------------------------------------------------- /rustscan-debbuilder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:latest 2 | 3 | RUN git clone https://github.com/brandonskerritt/RustScan 4 | WORKDIR "/RustScan" 5 | RUN git pull --force 6 | RUN cargo install cargo-deb 7 | 8 | RUN apt update -y && apt upgrade -y 9 | RUN apt install libc6-dev-i386 -y 10 | RUN git clone --depth=1 https://github.com/raspberrypi/tools /raspberrypi-tools 11 | ENV PATH=/raspberrypi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/:$PATH 12 | ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc 13 | RUN mkdir /root/.cargo 14 | RUN echo "[target.arm-unknown-linux-gnueabihf]" >> /root/.cargo/config 15 | RUN echo "strip = { path = \"arm-linux-gnueabihf-strip\" }" >> /root/.cargo/config 16 | RUN echo "objcopy = { path = \"arm-linux-gnueabihf-objcopy\" }" >> /root/.cargo/config 17 | 18 | COPY ./entrypoint.sh /entrypoint.sh 19 | RUN chmod +x /entrypoint.sh 20 | ENTRYPOINT ["/entrypoint.sh"] 21 | -------------------------------------------------------------------------------- /rustscan-debbuilder/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /RustScan 4 | git pull --force 5 | 6 | #amd64 7 | cargo deb 8 | 9 | #arm64 10 | rustup target add arm-unknown-linux-gnueabihf 11 | cargo deb --target=arm-unknown-linux-gnueabihf 12 | 13 | #i386 14 | rustup target add i686-unknown-linux-gnu 15 | cargo deb --target=i686-unknown-linux-gnu 16 | 17 | find target/ -name \*.deb -exec cp {} /debs \; 18 | -------------------------------------------------------------------------------- /rustscan-debbuilder/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build -t rustscan-builder . || exit 3 | 4 | # This creates a volume which binds your currentdirectory/debs to 5 | # the location where the deb files get spat out in the container. 6 | # You don't need to worry about it. Just chmod +x run.sh && ./run.sh and 7 | # you'll get yer .deb file in a few minutes. It runs faster after you've used it the first time. 8 | docker run -v "$(pwd)/debs:/debs" rustscan-builder 9 | -------------------------------------------------------------------------------- /src/benchmark/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Usage 2 | //! ``` 3 | //! // Initiate Benchmark vector 4 | //! let mut bm = Benchmark::init(); 5 | //! // Start named timer with name 6 | //! let mut example_bench = NamedTimer::start("Example Bench"); 7 | //! // Stop named timer 8 | //! example_bench.stop(); 9 | //! // Add named timer to Benchmarks 10 | //! bm.push(example_bench); 11 | //! // Print Benchmark Summary 12 | //! info!({}, bm.summary()); 13 | //! ``` 14 | use std::time::Instant; 15 | 16 | /// A Benchmark struct to hold NamedTimers with name, start and end Instants, 17 | #[derive(Debug)] 18 | pub struct Benchmark { 19 | named_timers: Vec, 20 | } 21 | 22 | impl Benchmark { 23 | pub fn init() -> Self { 24 | Self { 25 | named_timers: Vec::new(), 26 | } 27 | } 28 | pub fn push(&mut self, timer: NamedTimer) { 29 | self.named_timers.push(timer); 30 | } 31 | 32 | /// Summary of the benchmarks will destruct the vector, 33 | /// formats every element the same way and return 34 | /// a single String with all the available information 35 | /// for easy printing 36 | pub fn summary(&self) -> String { 37 | let mut summary = String::from("\nRustScan Benchmark Summary"); 38 | 39 | for timer in &self.named_timers { 40 | if timer.start.is_some() && timer.end.is_some() { 41 | let runtime_secs = timer 42 | .end 43 | .unwrap() 44 | .saturating_duration_since(timer.start.unwrap()) 45 | .as_secs_f32(); 46 | summary.push_str(&format!("\n{0: <10} | {1: <10}s", timer.name, runtime_secs)) 47 | } 48 | } 49 | summary 50 | } 51 | } 52 | 53 | /// The purpose of NamedTimer is to hold a name, 54 | /// start Instant and end Instant for a specific timer. 55 | /// The given name will be presented in the benchmark summary, 56 | /// start and end Instants will be used for calculating runtime. 57 | #[derive(Debug)] 58 | pub struct NamedTimer { 59 | name: &'static str, 60 | start: Option, 61 | end: Option, 62 | } 63 | 64 | impl NamedTimer { 65 | pub fn start(name: &'static str) -> Self { 66 | Self { 67 | name, 68 | start: Some(Instant::now()), 69 | end: None, 70 | } 71 | } 72 | pub fn end(&mut self) { 73 | self.end = Some(Instant::now()); 74 | } 75 | } 76 | 77 | #[test] 78 | fn benchmark() { 79 | let mut benchmarks = Benchmark::init(); 80 | let mut test_timer = NamedTimer::start("test"); 81 | std::thread::sleep(std::time::Duration::from_millis(100)); 82 | test_timer.end(); 83 | benchmarks.push(test_timer); 84 | benchmarks.push(NamedTimer::start("only_start")); 85 | assert!(benchmarks 86 | .summary() 87 | .contains("\nRustScan Benchmark Summary\ntest | 0.")); 88 | assert_ne!(benchmarks.summary().contains("only_start"), true); 89 | } 90 | -------------------------------------------------------------------------------- /src/input.rs: -------------------------------------------------------------------------------- 1 | use serde_derive::Deserialize; 2 | use std::collections::HashMap; 3 | use std::fs; 4 | use structopt::{clap::arg_enum, StructOpt}; 5 | 6 | const LOWEST_PORT_NUMBER: u16 = 1; 7 | const TOP_PORT_NUMBER: u16 = 65535; 8 | 9 | arg_enum! { 10 | /// Represents the strategy in which the port scanning will run. 11 | /// - Serial will run from start to end, for example 1 to 1_000. 12 | /// - Random will randomize the order in which ports will be scanned. 13 | #[derive(Deserialize, Debug, StructOpt, Clone, Copy, PartialEq)] 14 | pub enum ScanOrder { 15 | Serial, 16 | Random, 17 | } 18 | } 19 | 20 | arg_enum! { 21 | /// Represents the scripts variant. 22 | /// - none will avoid running any script, only portscan results will be shown. 23 | /// - default will run the default embedded nmap script, that's part of RustScan since the beginning. 24 | /// - custom will read the ScriptConfig file and the available scripts in the predefined folders 25 | #[derive(Deserialize, Debug, StructOpt, Clone, PartialEq, Copy)] 26 | pub enum ScriptsRequired { 27 | None, 28 | Default, 29 | Custom, 30 | } 31 | } 32 | 33 | /// Represents the range of ports to be scanned. 34 | #[derive(Deserialize, Debug, Clone, PartialEq)] 35 | pub struct PortRange { 36 | pub start: u16, 37 | pub end: u16, 38 | } 39 | 40 | #[cfg(not(tarpaulin_include))] 41 | fn parse_range(input: &str) -> Result { 42 | let range = input 43 | .split('-') 44 | .map(str::parse) 45 | .collect::, std::num::ParseIntError>>(); 46 | 47 | if range.is_err() { 48 | return Err(String::from( 49 | "the range format must be 'start-end'. Example: 1-1000.", 50 | )); 51 | } 52 | 53 | match range.unwrap().as_slice() { 54 | [start, end] => Ok(PortRange { 55 | start: *start, 56 | end: *end, 57 | }), 58 | _ => Err(String::from( 59 | "the range format must be 'start-end'. Example: 1-1000.", 60 | )), 61 | } 62 | } 63 | 64 | #[derive(StructOpt, Debug, Clone)] 65 | #[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)] 66 | #[allow(clippy::struct_excessive_bools)] 67 | /// Fast Port Scanner built in Rust. 68 | /// WARNING Do not use this program against sensitive infrastructure since the 69 | /// specified server may not be able to handle this many socket connections at once. 70 | /// - Discord https://discord.gg/GFrQsGy 71 | /// - GitHub https://github.com/RustScan/RustScan 72 | pub struct Opts { 73 | /// A list of comma separated CIDRs, IPs, or hosts to be scanned. 74 | #[structopt(short, long, use_delimiter = true)] 75 | pub addresses: Vec, 76 | 77 | /// A list of comma separed ports to be scanned. Example: 80,443,8080. 78 | #[structopt(short, long, use_delimiter = true)] 79 | pub ports: Option>, 80 | 81 | /// A range of ports with format start-end. Example: 1-1000. 82 | #[structopt(short, long, conflicts_with = "ports", parse(try_from_str = parse_range))] 83 | pub range: Option, 84 | 85 | /// Whether to ignore the configuration file or not. 86 | #[structopt(short, long)] 87 | pub no_config: bool, 88 | 89 | /// Greppable mode. Only output the ports. No Nmap. Useful for grep or outputting to a file. 90 | #[structopt(short, long)] 91 | pub greppable: bool, 92 | 93 | /// Accessible mode. Turns off features which negatively affect screen readers. 94 | #[structopt(long)] 95 | pub accessible: bool, 96 | 97 | /// The batch size for port scanning, it increases or slows the speed of 98 | /// scanning. Depends on the open file limit of your OS. If you do 65535 99 | /// it will do every port at the same time. Although, your OS may not 100 | /// support this. 101 | #[structopt(short, long, default_value = "4500")] 102 | pub batch_size: u16, 103 | 104 | /// The timeout in milliseconds before a port is assumed to be closed. 105 | #[structopt(short, long, default_value = "1500")] 106 | pub timeout: u32, 107 | 108 | /// The number of tries before a port is assumed to be closed. 109 | /// If set to 0, rustscan will correct it to 1. 110 | #[structopt(long, default_value = "1")] 111 | pub tries: u8, 112 | 113 | /// Automatically ups the ULIMIT with the value you provided. 114 | #[structopt(short, long)] 115 | pub ulimit: Option, 116 | 117 | /// The order of scanning to be performed. The "serial" option will 118 | /// scan ports in ascending order while the "random" option will scan 119 | /// ports randomly. 120 | #[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")] 121 | pub scan_order: ScanOrder, 122 | 123 | /// Level of scripting required for the run. 124 | #[structopt(long, possible_values = &ScriptsRequired::variants(), case_insensitive = true, default_value = "default")] 125 | pub scripts: ScriptsRequired, 126 | 127 | /// Use the top 1000 ports. 128 | #[structopt(long)] 129 | pub top: bool, 130 | 131 | /// The Script arguments to run. 132 | /// To use the argument -A, end RustScan's args with '-- -A'. 133 | /// Example: 'rustscan -T 1500 -a 127.0.0.1 -- -A -sC'. 134 | /// This command adds -Pn -vvv -p $PORTS automatically to nmap. 135 | /// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"") 136 | #[structopt(last = true)] 137 | pub command: Vec, 138 | } 139 | 140 | #[cfg(not(tarpaulin_include))] 141 | impl Opts { 142 | pub fn read() -> Self { 143 | let mut opts = Opts::from_args(); 144 | 145 | if opts.ports.is_none() && opts.range.is_none() { 146 | opts.range = Some(PortRange { 147 | start: LOWEST_PORT_NUMBER, 148 | end: TOP_PORT_NUMBER, 149 | }); 150 | } 151 | 152 | opts 153 | } 154 | 155 | /// Reads the command line arguments into an Opts struct and merge 156 | /// values found within the user configuration file. 157 | pub fn merge(&mut self, config: &Config) { 158 | if !self.no_config { 159 | self.merge_required(&config); 160 | self.merge_optional(&config); 161 | } 162 | } 163 | 164 | fn merge_required(&mut self, config: &Config) { 165 | macro_rules! merge_required { 166 | ($($field: ident),+) => { 167 | $( 168 | if let Some(e) = &config.$field { 169 | self.$field = e.clone(); 170 | } 171 | )+ 172 | } 173 | } 174 | 175 | merge_required!( 176 | addresses, greppable, accessible, batch_size, timeout, tries, scan_order, scripts, 177 | command 178 | ); 179 | } 180 | 181 | fn merge_optional(&mut self, config: &Config) { 182 | macro_rules! merge_optional { 183 | ($($field: ident),+) => { 184 | $( 185 | if config.$field.is_some() { 186 | self.$field = config.$field.clone(); 187 | } 188 | )+ 189 | } 190 | } 191 | 192 | // Only use top ports when the user asks for them 193 | if self.top && config.ports.is_some() { 194 | let mut ports: Vec = Vec::with_capacity(config.ports.clone().unwrap().len()); 195 | for entry in config.ports.clone().unwrap().keys() { 196 | ports.push(entry.parse().unwrap()) 197 | } 198 | self.ports = Some(ports); 199 | } 200 | 201 | merge_optional!(range, ulimit); 202 | } 203 | } 204 | 205 | /// Struct used to deserialize the options specified within our config file. 206 | /// These will be further merged with our command line arguments in order to 207 | /// generate the final Opts struct. 208 | #[cfg(not(tarpaulin_include))] 209 | #[derive(Debug, Deserialize)] 210 | pub struct Config { 211 | addresses: Option>, 212 | ports: Option>, 213 | range: Option, 214 | greppable: Option, 215 | accessible: Option, 216 | batch_size: Option, 217 | timeout: Option, 218 | tries: Option, 219 | ulimit: Option, 220 | scan_order: Option, 221 | command: Option>, 222 | scripts: Option, 223 | } 224 | 225 | #[cfg(not(tarpaulin_include))] 226 | impl Config { 227 | /// Reads the configuration file with TOML format and parses it into a 228 | /// Config struct. 229 | /// 230 | /// # Format 231 | /// 232 | /// addresses = ["127.0.0.1", "127.0.0.1"] 233 | /// ports = [80, 443, 8080] 234 | /// greppable = true 235 | /// scan_order: "Serial" 236 | /// 237 | pub fn read() -> Self { 238 | let mut home_dir = match dirs::home_dir() { 239 | Some(dir) => dir, 240 | None => panic!("Could not infer config file path."), 241 | }; 242 | home_dir.push(".rustscan.toml"); 243 | 244 | let mut content = String::new(); 245 | if home_dir.exists() { 246 | content = match fs::read_to_string(home_dir) { 247 | Ok(content) => content, 248 | Err(_) => String::new(), 249 | } 250 | } 251 | 252 | let config: Config = match toml::from_str(&content) { 253 | Ok(config) => config, 254 | Err(e) => { 255 | println!("Found {} in configuration file.\nAborting scan.\n", e); 256 | std::process::exit(1); 257 | } 258 | }; 259 | 260 | config 261 | } 262 | } 263 | 264 | #[cfg(test)] 265 | mod tests { 266 | use super::{Config, Opts, PortRange, ScanOrder, ScriptsRequired}; 267 | impl Config { 268 | fn default() -> Self { 269 | Self { 270 | addresses: Some(vec!["127.0.0.1".to_owned()]), 271 | ports: None, 272 | range: None, 273 | greppable: Some(true), 274 | batch_size: Some(25_000), 275 | timeout: Some(1_000), 276 | tries: Some(1), 277 | ulimit: None, 278 | command: Some(vec!["-A".to_owned()]), 279 | accessible: Some(true), 280 | scan_order: Some(ScanOrder::Random), 281 | scripts: None, 282 | } 283 | } 284 | } 285 | 286 | impl Opts { 287 | pub fn default() -> Self { 288 | Self { 289 | addresses: vec![], 290 | ports: None, 291 | range: None, 292 | greppable: true, 293 | batch_size: 0, 294 | timeout: 0, 295 | tries: 0, 296 | ulimit: None, 297 | command: vec![], 298 | accessible: false, 299 | scan_order: ScanOrder::Serial, 300 | no_config: true, 301 | top: false, 302 | scripts: ScriptsRequired::Default, 303 | } 304 | } 305 | } 306 | 307 | #[test] 308 | fn opts_no_merge_when_config_is_ignored() { 309 | let mut opts = Opts::default(); 310 | let config = Config::default(); 311 | 312 | opts.merge(&config); 313 | 314 | assert_eq!(opts.addresses, vec![] as Vec); 315 | assert_eq!(opts.greppable, true); 316 | assert_eq!(opts.accessible, false); 317 | assert_eq!(opts.timeout, 0); 318 | assert_eq!(opts.command, vec![] as Vec); 319 | assert_eq!(opts.scan_order, ScanOrder::Serial); 320 | } 321 | 322 | #[test] 323 | fn opts_merge_required_arguments() { 324 | let mut opts = Opts::default(); 325 | let config = Config::default(); 326 | 327 | opts.merge_required(&config); 328 | 329 | assert_eq!(opts.addresses, config.addresses.unwrap()); 330 | assert_eq!(opts.greppable, config.greppable.unwrap()); 331 | assert_eq!(opts.timeout, config.timeout.unwrap()); 332 | assert_eq!(opts.command, config.command.unwrap()); 333 | assert_eq!(opts.accessible, config.accessible.unwrap()); 334 | assert_eq!(opts.scan_order, config.scan_order.unwrap()); 335 | assert_eq!(opts.scripts, ScriptsRequired::Default) 336 | } 337 | 338 | #[test] 339 | fn opts_merge_optional_arguments() { 340 | let mut opts = Opts::default(); 341 | let mut config = Config::default(); 342 | config.range = Some(PortRange { 343 | start: 1, 344 | end: 1_000, 345 | }); 346 | config.ulimit = Some(1_000); 347 | 348 | opts.merge_optional(&config); 349 | 350 | assert_eq!(opts.range, config.range); 351 | assert_eq!(opts.ulimit, config.ulimit); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(clippy::all)] 2 | #![warn(clippy::pedantic)] 3 | #![allow(clippy::doc_markdown, clippy::if_not_else, clippy::non_ascii_literal)] 4 | 5 | extern crate shell_words; 6 | 7 | mod tui; 8 | 9 | mod input; 10 | use input::{Config, Opts, PortRange, ScanOrder, ScriptsRequired}; 11 | 12 | mod scanner; 13 | use scanner::Scanner; 14 | 15 | mod port_strategy; 16 | use port_strategy::PortStrategy; 17 | 18 | mod benchmark; 19 | use benchmark::{Benchmark, NamedTimer}; 20 | 21 | mod scripts; 22 | use scripts::{init_scripts, Script, ScriptFile}; 23 | 24 | use cidr_utils::cidr::IpCidr; 25 | use colorful::{Color, Colorful}; 26 | use futures::executor::block_on; 27 | use rlimit::{getrlimit, setrlimit, RawRlim, Resource, Rlim}; 28 | use std::collections::HashMap; 29 | use std::convert::TryInto; 30 | use std::fs::File; 31 | use std::io::{prelude::*, BufReader}; 32 | use std::net::{IpAddr, ToSocketAddrs}; 33 | use std::path::Path; 34 | use std::string::ToString; 35 | use std::time::Duration; 36 | use trust_dns_resolver::{ 37 | config::{ResolverConfig, ResolverOpts}, 38 | Resolver, 39 | }; 40 | 41 | extern crate colorful; 42 | extern crate dirs; 43 | 44 | // Average value for Ubuntu 45 | const DEFAULT_FILE_DESCRIPTORS_LIMIT: RawRlim = 8000; 46 | // Safest batch size based on experimentation 47 | const AVERAGE_BATCH_SIZE: RawRlim = 3000; 48 | 49 | #[macro_use] 50 | extern crate log; 51 | 52 | #[cfg(not(tarpaulin_include))] 53 | #[allow(clippy::too_many_lines)] 54 | /// Faster Nmap scanning with Rust 55 | /// If you're looking for the actual scanning, check out the module Scanner 56 | fn main() { 57 | env_logger::init(); 58 | let mut benchmarks = Benchmark::init(); 59 | let mut rustscan_bench = NamedTimer::start("RustScan"); 60 | 61 | let mut opts: Opts = Opts::read(); 62 | let config = Config::read(); 63 | opts.merge(&config); 64 | 65 | debug!("Main() `opts` arguments are {:?}", opts); 66 | 67 | let scripts_to_run: Vec = match init_scripts(opts.scripts) { 68 | Ok(scripts_to_run) => scripts_to_run, 69 | Err(e) => { 70 | warning!( 71 | format!("Initiating scripts failed!\n{}", e.to_string()), 72 | opts.greppable, 73 | opts.accessible 74 | ); 75 | std::process::exit(1); 76 | } 77 | }; 78 | 79 | debug!("Scripts initialized {:?}", &scripts_to_run); 80 | 81 | if !opts.greppable && !opts.accessible { 82 | print_opening(&opts); 83 | } 84 | 85 | let ips: Vec = parse_addresses(&opts); 86 | 87 | if ips.is_empty() { 88 | warning!( 89 | "No IPs could be resolved, aborting scan.", 90 | opts.greppable, 91 | opts.accessible 92 | ); 93 | std::process::exit(1); 94 | } 95 | 96 | let ulimit: RawRlim = adjust_ulimit_size(&opts); 97 | let batch_size: u16 = infer_batch_size(&opts, ulimit); 98 | 99 | let scanner = Scanner::new( 100 | &ips, 101 | batch_size, 102 | Duration::from_millis(opts.timeout.into()), 103 | opts.tries, 104 | opts.greppable, 105 | PortStrategy::pick(&opts.range, opts.ports, opts.scan_order), 106 | opts.accessible, 107 | ); 108 | debug!("Scanner finished building: {:?}", scanner); 109 | 110 | let mut portscan_bench = NamedTimer::start("Portscan"); 111 | let scan_result = block_on(scanner.run()); 112 | portscan_bench.end(); 113 | benchmarks.push(portscan_bench); 114 | 115 | let mut ports_per_ip = HashMap::new(); 116 | 117 | for socket in scan_result { 118 | ports_per_ip 119 | .entry(socket.ip()) 120 | .or_insert_with(Vec::new) 121 | .push(socket.port()); 122 | } 123 | 124 | for ip in ips { 125 | if ports_per_ip.contains_key(&ip) { 126 | continue; 127 | } 128 | 129 | // If we got here it means the IP was not found within the HashMap, this 130 | // means the scan couldn't find any open ports for it. 131 | 132 | let x = format!("Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size. 133 | \n*I used {} batch size, consider lowering it with {} or a comfortable number for your system. 134 | \n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n", 135 | ip, 136 | opts.batch_size, 137 | "'rustscan -b -a '"); 138 | warning!(x, opts.greppable, opts.accessible); 139 | } 140 | 141 | let mut script_bench = NamedTimer::start("Scripts"); 142 | for (ip, ports) in &ports_per_ip { 143 | let vec_str_ports: Vec = ports.iter().map(ToString::to_string).collect(); 144 | 145 | // nmap port style is 80,443. Comma separated with no spaces. 146 | let ports_str = vec_str_ports.join(","); 147 | 148 | // if option scripts is none, no script will be spawned 149 | if opts.greppable || opts.scripts == ScriptsRequired::None { 150 | println!("{} -> [{}]", &ip, ports_str); 151 | continue; 152 | } 153 | detail!("Starting Script(s)", opts.greppable, opts.accessible); 154 | 155 | // Run all the scripts we found and parsed based on the script config file tags field. 156 | for mut script_f in scripts_to_run.clone() { 157 | // This part allows us to add commandline arguments to the Script call_format, appending them to the end of the command. 158 | if !opts.command.is_empty() { 159 | let user_extra_args = &opts.command.join(" "); 160 | debug!("Extra args vec {:?}", user_extra_args); 161 | if script_f.call_format.is_some() { 162 | let mut call_f = script_f.call_format.unwrap(); 163 | call_f.push(' '); 164 | call_f.push_str(user_extra_args); 165 | output!( 166 | format!("Running script {:?} on ip {}\nDepending on the complexity of the script, results may take some time to appear.", call_f, &ip), 167 | opts.greppable, 168 | opts.accessible 169 | ); 170 | debug!("Call format {}", call_f); 171 | script_f.call_format = Some(call_f); 172 | } 173 | } 174 | 175 | // Building the script with the arguments from the ScriptFile, and ip-ports. 176 | let script = Script::build( 177 | script_f.path, 178 | *ip, 179 | ports.to_vec(), 180 | script_f.port, 181 | script_f.ports_separator, 182 | script_f.tags, 183 | script_f.call_format, 184 | ); 185 | match script.run() { 186 | Ok(script_result) => { 187 | detail!(script_result.to_string(), opts.greppable, opts.accessible); 188 | } 189 | Err(e) => { 190 | warning!( 191 | &format!("Error {}", e.to_string()), 192 | opts.greppable, 193 | opts.accessible 194 | ); 195 | } 196 | } 197 | } 198 | } 199 | 200 | // To use the runtime benchmark, run the process as: RUST_LOG=info ./rustscan 201 | script_bench.end(); 202 | benchmarks.push(script_bench); 203 | rustscan_bench.end(); 204 | benchmarks.push(rustscan_bench); 205 | debug!("Benchmarks raw {:?}", benchmarks); 206 | info!("{}", benchmarks.summary()); 207 | } 208 | 209 | /// Prints the opening title of RustScan 210 | fn print_opening(opts: &Opts) { 211 | debug!("Printing opening"); 212 | let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-. 213 | | {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| | 214 | | .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ | 215 | `-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-' 216 | The Modern Day Port Scanner."#; 217 | println!("{}", s.gradient(Color::Green).bold()); 218 | let info = r#"________________________________________ 219 | : https://discord.gg/GFrQsGy : 220 | : https://github.com/RustScan/RustScan : 221 | --------------------------------------"#; 222 | println!("{}", info.gradient(Color::Yellow).bold()); 223 | funny_opening!(); 224 | 225 | let config_path = dirs::home_dir() 226 | .expect("Could not infer config file path.") 227 | .join(".rustscan.toml"); 228 | 229 | detail!( 230 | format!("The config file is expected to be at {:?}", config_path), 231 | opts.greppable, 232 | opts.accessible 233 | ); 234 | } 235 | 236 | /// Goes through all possible IP inputs (files or via argparsing) 237 | /// Parses the string(s) into IPs 238 | fn parse_addresses(input: &Opts) -> Vec { 239 | let mut ips: Vec = Vec::new(); 240 | let mut unresolved_addresses: Vec<&str> = Vec::new(); 241 | let backup_resolver = 242 | Resolver::new(ResolverConfig::cloudflare_tls(), ResolverOpts::default()).unwrap(); 243 | 244 | for address in &input.addresses { 245 | let parsed_ips = parse_address(address, &backup_resolver); 246 | if !parsed_ips.is_empty() { 247 | ips.extend(parsed_ips); 248 | } else { 249 | unresolved_addresses.push(address); 250 | } 251 | } 252 | 253 | // If we got to this point this can only be a file path or the wrong input. 254 | for file_path in unresolved_addresses { 255 | let file_path = Path::new(file_path); 256 | 257 | if !file_path.is_file() { 258 | warning!( 259 | format!("Host {:?} could not be resolved.", file_path), 260 | input.greppable, 261 | input.accessible 262 | ); 263 | 264 | continue; 265 | } 266 | 267 | if let Ok(x) = read_ips_from_file(file_path, &backup_resolver) { 268 | ips.extend(x); 269 | } else { 270 | warning!( 271 | format!("Host {:?} could not be resolved.", file_path), 272 | input.greppable, 273 | input.accessible 274 | ); 275 | } 276 | } 277 | 278 | ips 279 | } 280 | 281 | /// Given a string, parse it as an host, IP address, or CIDR. 282 | /// This allows us to pass files as hosts or cidr or IPs easily 283 | /// Call this everytime you have a possible IP_or_host 284 | fn parse_address(address: &str, resolver: &Resolver) -> Vec { 285 | IpCidr::from_str(&address) 286 | .map(|cidr| cidr.iter().collect()) 287 | .ok() 288 | .or_else(|| { 289 | format!("{}:{}", &address, 80) 290 | .to_socket_addrs() 291 | .ok() 292 | .map(|mut iter| vec![iter.next().unwrap().ip()]) 293 | }) 294 | .unwrap_or_else(|| resolve_ips_from_host(address, resolver)) 295 | } 296 | 297 | /// Uses DNS to get the IPS assiocated with host 298 | fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec { 299 | let mut ips: Vec = Vec::new(); 300 | 301 | if let Ok(addrs) = source.to_socket_addrs() { 302 | for ip in addrs { 303 | ips.push(ip.ip()); 304 | } 305 | } else if let Ok(addrs) = backup_resolver.lookup_ip(&source) { 306 | ips.extend(addrs.iter()); 307 | } 308 | 309 | ips 310 | } 311 | 312 | #[cfg(not(tarpaulin_include))] 313 | /// Parses an input file of IPs and uses those 314 | fn read_ips_from_file( 315 | ips: &std::path::Path, 316 | backup_resolver: &Resolver, 317 | ) -> Result, std::io::Error> { 318 | let file = File::open(ips)?; 319 | let reader = BufReader::new(file); 320 | 321 | let mut ips: Vec = Vec::new(); 322 | 323 | for address_line in reader.lines() { 324 | if let Ok(address) = address_line { 325 | ips.extend(parse_address(&address, backup_resolver)); 326 | } else { 327 | debug!("Line in file is not valid"); 328 | } 329 | } 330 | 331 | Ok(ips) 332 | } 333 | 334 | fn adjust_ulimit_size(opts: &Opts) -> RawRlim { 335 | if opts.ulimit.is_some() { 336 | let limit: Rlim = Rlim::from_raw(opts.ulimit.unwrap()); 337 | 338 | if setrlimit(Resource::NOFILE, limit, limit).is_ok() { 339 | detail!( 340 | format!("Automatically increasing ulimit value to {}.", limit), 341 | opts.greppable, 342 | opts.accessible 343 | ); 344 | } else { 345 | warning!( 346 | "ERROR. Failed to set ulimit value.", 347 | opts.greppable, 348 | opts.accessible 349 | ); 350 | } 351 | } 352 | 353 | let (rlim, _) = getrlimit(Resource::NOFILE).unwrap(); 354 | 355 | rlim.as_raw() 356 | } 357 | 358 | fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 { 359 | let mut batch_size: RawRlim = opts.batch_size.into(); 360 | 361 | // Adjust the batch size when the ulimit value is lower than the desired batch size 362 | if ulimit < batch_size { 363 | warning!("File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers", 364 | opts.greppable, opts.accessible 365 | ); 366 | 367 | // When the OS supports high file limits like 8000, but the user 368 | // selected a batch size higher than this we should reduce it to 369 | // a lower number. 370 | if ulimit < AVERAGE_BATCH_SIZE { 371 | // ulimit is smaller than aveage batch size 372 | // user must have very small ulimit 373 | // decrease batch size to half of ulimit 374 | 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); 375 | info!("Halving batch_size because ulimit is smaller than average batch size"); 376 | batch_size = ulimit / 2 377 | } else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT { 378 | info!("Batch size is now average batch size"); 379 | batch_size = AVERAGE_BATCH_SIZE 380 | } else { 381 | batch_size = ulimit - 100 382 | } 383 | } 384 | // When the ulimit is higher than the batch size let the user know that the 385 | // batch size can be increased unless they specified the ulimit themselves. 386 | else if ulimit + 2 > batch_size && (opts.ulimit.is_none()) { 387 | detail!(format!("File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", ulimit - 100), 388 | opts.greppable, opts.accessible); 389 | } 390 | 391 | batch_size 392 | .try_into() 393 | .expect("Couldn't fit the batch size into a u16.") 394 | } 395 | 396 | #[cfg(test)] 397 | mod tests { 398 | use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts}; 399 | use std::net::Ipv4Addr; 400 | 401 | #[test] 402 | fn batch_size_lowered() { 403 | let mut opts = Opts::default(); 404 | opts.batch_size = 50_000; 405 | let batch_size = infer_batch_size(&opts, 120); 406 | 407 | assert!(batch_size < opts.batch_size); 408 | } 409 | 410 | #[test] 411 | fn batch_size_lowered_average_size() { 412 | let mut opts = Opts::default(); 413 | opts.batch_size = 50_000; 414 | let batch_size = infer_batch_size(&opts, 9_000); 415 | 416 | assert!(batch_size == 3_000); 417 | } 418 | #[test] 419 | fn batch_size_equals_ulimit_lowered() { 420 | // because ulimit and batch size are same size, batch size is lowered 421 | // to ULIMIT - 100 422 | let mut opts = Opts::default(); 423 | opts.batch_size = 50_000; 424 | let batch_size = infer_batch_size(&opts, 5_000); 425 | 426 | assert!(batch_size == 4_900); 427 | } 428 | #[test] 429 | fn batch_size_adjusted_2000() { 430 | // ulimit == batch_size 431 | let mut opts = Opts::default(); 432 | opts.batch_size = 50_000; 433 | opts.ulimit = Some(2_000); 434 | let batch_size = adjust_ulimit_size(&opts); 435 | 436 | assert!(batch_size == 2_000); 437 | } 438 | #[test] 439 | fn test_print_opening_no_panic() { 440 | let mut opts = Opts::default(); 441 | opts.ulimit = Some(2_000); 442 | // print opening should not panic 443 | print_opening(&opts); 444 | } 445 | 446 | #[test] 447 | fn test_high_ulimit_no_greppable_mode() { 448 | let mut opts = Opts::default(); 449 | opts.batch_size = 10; 450 | opts.greppable = false; 451 | 452 | let batch_size = infer_batch_size(&opts, 1_000_000); 453 | 454 | assert!(batch_size == opts.batch_size); 455 | } 456 | 457 | #[test] 458 | fn parse_correct_addresses() { 459 | let mut opts = Opts::default(); 460 | opts.addresses = vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()]; 461 | let ips = parse_addresses(&opts); 462 | 463 | assert_eq!( 464 | ips, 465 | [ 466 | Ipv4Addr::new(127, 0, 0, 1), 467 | Ipv4Addr::new(192, 168, 0, 0), 468 | Ipv4Addr::new(192, 168, 0, 1), 469 | Ipv4Addr::new(192, 168, 0, 2), 470 | Ipv4Addr::new(192, 168, 0, 3) 471 | ] 472 | ); 473 | } 474 | 475 | #[test] 476 | fn parse_correct_host_addresses() { 477 | let mut opts = Opts::default(); 478 | opts.addresses = vec!["google.com".to_owned()]; 479 | let ips = parse_addresses(&opts); 480 | 481 | assert_eq!(ips.len(), 1); 482 | } 483 | 484 | #[test] 485 | fn parse_correct_and_incorrect_addresses() { 486 | let mut opts = Opts::default(); 487 | opts.addresses = vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()]; 488 | let ips = parse_addresses(&opts); 489 | 490 | assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]); 491 | } 492 | 493 | #[test] 494 | fn parse_incorrect_addresses() { 495 | let mut opts = Opts::default(); 496 | opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()]; 497 | let ips = parse_addresses(&opts); 498 | 499 | assert_eq!(ips.is_empty(), true); 500 | } 501 | #[test] 502 | fn parse_hosts_file_and_incorrect_hosts() { 503 | // Host file contains IP, Hosts, incorrect IPs, incorrect hosts 504 | let mut opts = Opts::default(); 505 | opts.addresses = vec!["fixtures/hosts.txt".to_owned()]; 506 | let ips = parse_addresses(&opts); 507 | assert_eq!(ips.len(), 3); 508 | } 509 | 510 | #[test] 511 | fn parse_empty_hosts_file() { 512 | // Host file contains IP, Hosts, incorrect IPs, incorrect hosts 513 | let mut opts = Opts::default(); 514 | opts.addresses = vec!["fixtures/empty_hosts.txt".to_owned()]; 515 | let ips = parse_addresses(&opts); 516 | assert_eq!(ips.len(), 0); 517 | } 518 | 519 | #[test] 520 | fn parse_naughty_host_file() { 521 | // Host file contains IP, Hosts, incorrect IPs, incorrect hosts 522 | let mut opts = Opts::default(); 523 | opts.addresses = vec!["fixtures/naughty_string.txt".to_owned()]; 524 | let ips = parse_addresses(&opts); 525 | assert_eq!(ips.len(), 0); 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /src/port_strategy/mod.rs: -------------------------------------------------------------------------------- 1 | mod range_iterator; 2 | use super::{PortRange, ScanOrder}; 3 | use rand::seq::SliceRandom; 4 | use rand::thread_rng; 5 | use range_iterator::RangeIterator; 6 | 7 | /// Represents options of port scanning. 8 | /// 9 | /// Right now all these options involve ranges, but in the future 10 | /// it will also contain custom lists of ports. 11 | #[derive(Debug)] 12 | pub enum PortStrategy { 13 | Manual(Vec), 14 | Serial(SerialRange), 15 | Random(RandomRange), 16 | } 17 | 18 | impl PortStrategy { 19 | pub fn pick(range: &Option, ports: Option>, order: ScanOrder) -> Self { 20 | match order { 21 | ScanOrder::Serial if ports.is_none() => { 22 | let range = range.as_ref().unwrap(); 23 | PortStrategy::Serial(SerialRange { 24 | start: range.start, 25 | end: range.end, 26 | }) 27 | } 28 | ScanOrder::Random if ports.is_none() => { 29 | let range = range.as_ref().unwrap(); 30 | PortStrategy::Random(RandomRange { 31 | start: range.start, 32 | end: range.end, 33 | }) 34 | } 35 | ScanOrder::Serial => PortStrategy::Manual(ports.unwrap()), 36 | ScanOrder::Random => { 37 | let mut rng = thread_rng(); 38 | let mut ports = ports.unwrap(); 39 | ports.shuffle(&mut rng); 40 | PortStrategy::Manual(ports) 41 | } 42 | } 43 | } 44 | 45 | pub fn order(&self) -> Vec { 46 | match self { 47 | PortStrategy::Manual(ports) => ports.to_vec(), 48 | PortStrategy::Serial(range) => range.generate(), 49 | PortStrategy::Random(range) => range.generate(), 50 | } 51 | } 52 | } 53 | 54 | /// Trait associated with a port strategy. Each PortStrategy must be able 55 | /// to generate an order for future port scanning. 56 | trait RangeOrder { 57 | fn generate(&self) -> Vec; 58 | } 59 | 60 | /// As the name implies SerialRange will always generate a vector in 61 | /// ascending order. 62 | #[derive(Debug)] 63 | pub struct SerialRange { 64 | start: u16, 65 | end: u16, 66 | } 67 | 68 | impl RangeOrder for SerialRange { 69 | fn generate(&self) -> Vec { 70 | (self.start..self.end).collect() 71 | } 72 | } 73 | 74 | /// As the name implies RandomRange will always generate a vector with 75 | /// a random order. This vector is built following the LCG algorithm. 76 | #[derive(Debug)] 77 | pub struct RandomRange { 78 | start: u16, 79 | end: u16, 80 | } 81 | 82 | impl RangeOrder for RandomRange { 83 | // Right now using RangeIterator and generating a range + shuffling the 84 | // vector is pretty much the same. The advantages of it will come once 85 | // we have to generate different ranges for different IPs without storing 86 | // actual vectors. 87 | // 88 | // Another benefit of RangeIterator is that it always generate a range with 89 | // a certain distance between the items in the Array. The chances of having 90 | // port numbers close to each other are pretty slim due to the way the 91 | // algorithm works. 92 | fn generate(&self) -> Vec { 93 | RangeIterator::new(self.start.into(), self.end.into()).collect() 94 | } 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use super::PortStrategy; 100 | use crate::{PortRange, ScanOrder}; 101 | 102 | #[test] 103 | fn serial_strategy_with_range() { 104 | let range = PortRange { start: 1, end: 100 }; 105 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Serial); 106 | let result = strategy.order(); 107 | let expected_range = (1..100).into_iter().collect::>(); 108 | assert_eq!(expected_range, result); 109 | } 110 | #[test] 111 | fn random_strategy_with_range() { 112 | let range = PortRange { start: 1, end: 100 }; 113 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); 114 | let mut result = strategy.order(); 115 | let expected_range = (1..100).into_iter().collect::>(); 116 | assert_ne!(expected_range, result); 117 | 118 | result.sort(); 119 | assert_eq!(expected_range, result); 120 | } 121 | 122 | #[test] 123 | fn serial_strategy_with_ports() { 124 | let strategy = PortStrategy::pick(&None, Some(vec![80, 443]), ScanOrder::Serial); 125 | let result = strategy.order(); 126 | assert_eq!(vec![80, 443], result); 127 | } 128 | 129 | #[test] 130 | fn random_strategy_with_ports() { 131 | let strategy = PortStrategy::pick(&None, Some((1..10).collect()), ScanOrder::Random); 132 | let mut result = strategy.order(); 133 | let expected_range = (1..10).into_iter().collect::>(); 134 | assert_ne!(expected_range, result); 135 | 136 | result.sort(); 137 | assert_eq!(expected_range, result); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/port_strategy/range_iterator.rs: -------------------------------------------------------------------------------- 1 | use gcd::Gcd; 2 | use rand::Rng; 3 | use std::convert::TryInto; 4 | 5 | pub struct RangeIterator { 6 | active: bool, 7 | normalized_end: u32, 8 | normalized_first_pick: u32, 9 | normalized_pick: u32, 10 | actual_start: u32, 11 | step: u32, 12 | } 13 | 14 | /// An iterator that follows the `Linear Congruential Generator` algorithm. 15 | /// 16 | /// For more information: https://en.wikipedia.org/wiki/Linear_congruential_generator 17 | impl RangeIterator { 18 | /// Receives the the start and end of a range and normalize 19 | /// these values before selecting a coprime for the end of the range 20 | /// which will server as the step for the algorithm. 21 | /// 22 | /// For example, the range `1000-2500` will be normalized to `0-1500` 23 | /// before going through the algorithm. 24 | pub fn new(start: u32, end: u32) -> Self { 25 | let normalized_end = end - start; 26 | let step = pick_random_coprime(normalized_end); 27 | 28 | // Randomly choose a number within the range to be the first 29 | // and assign it as a pick. 30 | let mut rng = rand::thread_rng(); 31 | let normalized_first_pick = rng.gen_range(0, normalized_end); 32 | 33 | Self { 34 | active: true, 35 | normalized_end, 36 | step, 37 | normalized_first_pick, 38 | normalized_pick: normalized_first_pick, 39 | actual_start: start, 40 | } 41 | } 42 | } 43 | 44 | impl Iterator for RangeIterator { 45 | type Item = u16; 46 | 47 | // The next step is always bound by the formula: N+1 = (N + STEP) % TOP_OF_THE_RANGE 48 | // It will only stop once we generate a number equal to the first generated number. 49 | fn next(&mut self) -> Option { 50 | if !self.active { 51 | return None; 52 | } 53 | 54 | let current_pick = self.normalized_pick; 55 | let next_pick = (current_pick + self.step) % self.normalized_end; 56 | 57 | // If the next pick is equal to the first pick this means that 58 | // we have iterated through the entire range. 59 | if next_pick == self.normalized_first_pick { 60 | self.active = false; 61 | } 62 | 63 | self.normalized_pick = next_pick; 64 | Some( 65 | (self.actual_start + current_pick) 66 | .try_into() 67 | .expect("Could not convert u32 to u16"), 68 | ) 69 | } 70 | } 71 | 72 | /// The probability that two random integers are coprime to one another 73 | /// works out to be around 61%, given that we can safely pick a random 74 | /// number and test it. Just in case we are having a bad day and we cannot 75 | /// pick a coprime number after 10 tries we just return "end - 1" which 76 | /// is guaranteed to be a coprime, but won't provide ideal randomization. 77 | /// 78 | /// We pick between "lower_range" and "upper_range" since values too close to 79 | /// the boundaries, which in these case are the "start" and "end" arguments 80 | /// would also provide non-ideal randomization as discussed on the paragraph 81 | /// above. 82 | fn pick_random_coprime(end: u32) -> u32 { 83 | let range_boundary = end / 4; 84 | let lower_range = range_boundary; 85 | let upper_range = end - range_boundary; 86 | let mut rng = rand::thread_rng(); 87 | let mut candidate = rng.gen_range(lower_range, upper_range); 88 | 89 | for _ in 0..10 { 90 | if end.gcd(candidate) == 1 { 91 | return candidate; 92 | } else { 93 | candidate = rng.gen_range(lower_range, upper_range); 94 | } 95 | } 96 | 97 | end - 1 98 | } 99 | 100 | #[cfg(test)] 101 | mod tests { 102 | use super::RangeIterator; 103 | 104 | #[test] 105 | fn range_iterator_iterates_through_the_entire_range() { 106 | let result = generate_sorted_range(1, 10); 107 | let expected_range = (1..10).into_iter().collect::>(); 108 | assert_eq!(expected_range, result); 109 | 110 | let result = generate_sorted_range(1, 100); 111 | let expected_range = (1..100).into_iter().collect::>(); 112 | assert_eq!(expected_range, result); 113 | 114 | let result = generate_sorted_range(1, 1000); 115 | let expected_range = (1..1000).into_iter().collect::>(); 116 | assert_eq!(expected_range, result); 117 | 118 | let result = generate_sorted_range(1, 65_535); 119 | let expected_range = (1..65_535).into_iter().collect::>(); 120 | assert_eq!(expected_range, result); 121 | 122 | let result = generate_sorted_range(1000, 2000); 123 | let expected_range = (1000..2000).into_iter().collect::>(); 124 | assert_eq!(expected_range, result); 125 | } 126 | 127 | fn generate_sorted_range(start: u32, end: u32) -> Vec { 128 | let range = RangeIterator::new(start, end); 129 | let mut result = range.into_iter().collect::>(); 130 | result.sort(); 131 | 132 | result 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/scanner/mod.rs: -------------------------------------------------------------------------------- 1 | use super::PortStrategy; 2 | 3 | mod socket_iterator; 4 | use socket_iterator::SocketIterator; 5 | 6 | use async_std::io; 7 | use async_std::net::TcpStream; 8 | use async_std::prelude::*; 9 | use colored::Colorize; 10 | use futures::stream::FuturesUnordered; 11 | use std::{ 12 | collections::HashSet, 13 | net::{IpAddr, Shutdown, SocketAddr}, 14 | num::NonZeroU8, 15 | time::Duration, 16 | }; 17 | 18 | /// The class for the scanner 19 | /// IP is data type IpAddr and is the IP address 20 | /// start & end is where the port scan starts and ends 21 | /// batch_size is how many ports at a time should be scanned 22 | /// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration. 23 | /// greppable is whether or not RustScan should print things, or wait until the end to print only the ip and open ports. 24 | #[cfg(not(tarpaulin_include))] 25 | #[derive(Debug)] 26 | pub struct Scanner { 27 | ips: Vec, 28 | batch_size: u16, 29 | timeout: Duration, 30 | tries: NonZeroU8, 31 | greppable: bool, 32 | port_strategy: PortStrategy, 33 | accessible: bool, 34 | } 35 | 36 | impl Scanner { 37 | pub fn new( 38 | ips: &[IpAddr], 39 | batch_size: u16, 40 | timeout: Duration, 41 | tries: u8, 42 | greppable: bool, 43 | port_strategy: PortStrategy, 44 | accessible: bool, 45 | ) -> Self { 46 | Self { 47 | batch_size, 48 | timeout, 49 | tries: NonZeroU8::new(std::cmp::max(tries, 1)).unwrap(), 50 | greppable, 51 | port_strategy, 52 | ips: ips.iter().map(ToOwned::to_owned).collect(), 53 | accessible, 54 | } 55 | } 56 | 57 | /// Runs scan_range with chunk sizes 58 | /// If you want to run RustScan normally, this is the entry point used 59 | /// Returns all open ports as Vec 60 | pub async fn run(&self) -> Vec { 61 | let ports: Vec = self.port_strategy.order(); 62 | let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports); 63 | let mut open_sockets: Vec = Vec::new(); 64 | let mut ftrs = FuturesUnordered::new(); 65 | let mut errors: HashSet = HashSet::with_capacity(self.ips.len() * 1000); 66 | 67 | for _ in 0..self.batch_size { 68 | if let Some(socket) = socket_iterator.next() { 69 | ftrs.push(self.scan_socket(socket)); 70 | } else { 71 | break; 72 | } 73 | } 74 | 75 | debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ", 76 | self.batch_size, 77 | self.ips.len(), 78 | &ports.len(), 79 | (self.ips.len() * ports.len())); 80 | 81 | while let Some(result) = ftrs.next().await { 82 | if let Some(socket) = socket_iterator.next() { 83 | ftrs.push(self.scan_socket(socket)); 84 | } 85 | 86 | match result { 87 | Ok(socket) => open_sockets.push(socket), 88 | Err(e) => { 89 | let error_string = e.to_string(); 90 | if errors.len() < self.ips.len() * 1000 { 91 | errors.insert(error_string); 92 | } 93 | } 94 | } 95 | } 96 | debug!("Typical socket connection errors {:?}", errors); 97 | debug!("Open Sockets found: {:?}", &open_sockets); 98 | open_sockets 99 | } 100 | 101 | /// Given a socket, scan it self.tries times. 102 | /// Turns the address into a SocketAddr 103 | /// Deals with the type 104 | /// If it experiences error ErrorKind::Other then too many files are open and it Panics! 105 | /// Else any other error, it returns the error in Result as a string 106 | /// If no errors occur, it returns the port number in Result to signify the port is open. 107 | /// This function mainly deals with the logic of Results handling. 108 | /// # Example 109 | /// 110 | /// self.scan_socket(socket) 111 | /// 112 | /// Note: `self` must contain `self.ip`. 113 | async fn scan_socket(&self, socket: SocketAddr) -> io::Result { 114 | let tries = self.tries.get(); 115 | 116 | for nr_try in 1..=tries { 117 | match self.connect(socket).await { 118 | Ok(x) => { 119 | debug!( 120 | "Connection was successful, shutting down stream {}", 121 | &socket 122 | ); 123 | if let Err(e) = x.shutdown(Shutdown::Both) { 124 | debug!("Shutdown stream error {}", &e); 125 | } 126 | if !self.greppable { 127 | if self.accessible { 128 | println!("Open {}", socket.to_string()); 129 | } else { 130 | println!("Open {}", socket.to_string().purple()); 131 | } 132 | } 133 | 134 | debug!("Return Ok after {} tries", nr_try); 135 | return Ok(socket); 136 | } 137 | Err(e) => { 138 | let mut error_string = e.to_string(); 139 | 140 | if error_string.to_lowercase().contains("too many open files") { 141 | panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500."); 142 | } 143 | 144 | if nr_try == tries { 145 | error_string.push(' '); 146 | error_string.push_str(&socket.ip().to_string()); 147 | return Err(io::Error::new(io::ErrorKind::Other, error_string)); 148 | } 149 | } 150 | }; 151 | } 152 | unreachable!(); 153 | } 154 | 155 | /// Performs the connection to the socket with timeout 156 | /// # Example 157 | /// 158 | /// let port: u16 = 80 159 | /// // ip is an IpAddr type 160 | /// let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); 161 | /// let socket = SocketAddr::new(ip, port); 162 | /// self.connect(socket) 163 | /// // returns Result which is either Ok(stream) for port is open, or Er for port is closed. 164 | /// // Timeout occurs after self.timeout seconds 165 | /// 166 | async fn connect(&self, socket: SocketAddr) -> io::Result { 167 | let stream = io::timeout( 168 | self.timeout, 169 | async move { TcpStream::connect(socket).await }, 170 | ) 171 | .await?; 172 | Ok(stream) 173 | } 174 | } 175 | 176 | #[cfg(test)] 177 | mod tests { 178 | use super::*; 179 | use crate::{PortRange, ScanOrder}; 180 | use async_std::task::block_on; 181 | use std::{net::IpAddr, time::Duration}; 182 | 183 | #[test] 184 | fn scanner_runs() { 185 | // Makes sure the program still runs and doesn't panic 186 | let addrs = vec!["127.0.0.1".parse::().unwrap()]; 187 | let range = PortRange { 188 | start: 1, 189 | end: 1_000, 190 | }; 191 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); 192 | let scanner = Scanner::new( 193 | &addrs, 194 | 10, 195 | Duration::from_millis(100), 196 | 1, 197 | true, 198 | strategy, 199 | true, 200 | ); 201 | block_on(scanner.run()); 202 | // if the scan fails, it wouldn't be able to assert_eq! as it panicked! 203 | assert_eq!(1, 1); 204 | } 205 | #[test] 206 | fn ipv6_scanner_runs() { 207 | // Makes sure the program still runs and doesn't panic 208 | let addrs = vec!["::1".parse::().unwrap()]; 209 | let range = PortRange { 210 | start: 1, 211 | end: 1_000, 212 | }; 213 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); 214 | let scanner = Scanner::new( 215 | &addrs, 216 | 10, 217 | Duration::from_millis(100), 218 | 1, 219 | true, 220 | strategy, 221 | true, 222 | ); 223 | block_on(scanner.run()); 224 | // if the scan fails, it wouldn't be able to assert_eq! as it panicked! 225 | assert_eq!(1, 1); 226 | } 227 | #[test] 228 | fn quad_zero_scanner_runs() { 229 | let addrs = vec!["0.0.0.0".parse::().unwrap()]; 230 | let range = PortRange { 231 | start: 1, 232 | end: 1_000, 233 | }; 234 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); 235 | let scanner = Scanner::new( 236 | &addrs, 237 | 10, 238 | Duration::from_millis(100), 239 | 1, 240 | true, 241 | strategy, 242 | true, 243 | ); 244 | block_on(scanner.run()); 245 | assert_eq!(1, 1); 246 | } 247 | #[test] 248 | fn google_dns_runs() { 249 | let addrs = vec!["8.8.8.8".parse::().unwrap()]; 250 | let range = PortRange { 251 | start: 400, 252 | end: 445, 253 | }; 254 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); 255 | let scanner = Scanner::new( 256 | &addrs, 257 | 10, 258 | Duration::from_millis(100), 259 | 1, 260 | true, 261 | strategy, 262 | true, 263 | ); 264 | block_on(scanner.run()); 265 | assert_eq!(1, 1); 266 | } 267 | #[test] 268 | fn infer_ulimit_lowering_no_panic() { 269 | // Test behaviour on MacOS where ulimit is not automatically lowered 270 | let addrs = vec!["8.8.8.8".parse::().unwrap()]; 271 | 272 | // mac should have this automatically scaled down 273 | let range = PortRange { 274 | start: 400, 275 | end: 600, 276 | }; 277 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); 278 | let scanner = Scanner::new( 279 | &addrs, 280 | 10, 281 | Duration::from_millis(100), 282 | 1, 283 | true, 284 | strategy, 285 | true, 286 | ); 287 | block_on(scanner.run()); 288 | assert_eq!(1, 1); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/scanner/socket_iterator.rs: -------------------------------------------------------------------------------- 1 | use itertools::{iproduct, Product}; 2 | use std::net::{IpAddr, SocketAddr}; 3 | 4 | pub struct SocketIterator<'s> { 5 | // product_it is a cartesian product iterator over 6 | // the slices of ports and IP addresses. 7 | // 8 | // The IP/port order is intentionally reversed here since we want 9 | // the itertools::iproduct! macro below to generate the pairs with 10 | // all the IPs for one port before moving on to the next one 11 | // ("hold the port, go through all the IPs, then advance the port..."). 12 | // See also the comments in the iterator implementation for an example. 13 | product_it: 14 | Product>, Box>>, 15 | } 16 | 17 | /// An iterator that receives a slice of IPs and ports and returns a Socket 18 | /// for each IP and port pair until all of these combinations are exhausted. 19 | /// The goal of this iterator is to go over every IP and port combination 20 | /// wihout generating a big memory footprint. The alternative would be 21 | /// generating a vector containing all these combinations. 22 | impl<'s> SocketIterator<'s> { 23 | pub fn new(ips: &'s [IpAddr], ports: &'s [u16]) -> Self { 24 | let ports_it = Box::new(ports.iter()); 25 | let ips_it = Box::new(ips.iter()); 26 | Self { 27 | product_it: iproduct!(ports_it, ips_it), 28 | } 29 | } 30 | } 31 | 32 | impl<'s> Iterator for SocketIterator<'s> { 33 | type Item = SocketAddr; 34 | 35 | /// Returns a socket based on the combination of one of the provided 36 | /// IPs and ports or None when these combinations are exhausted. Every 37 | /// IP will have the same port until a port is incremented. 38 | /// 39 | /// let it = SocketIterator::new(&["127.0.0.1", "192.168.0.1"], &[80, 443]); 40 | /// it.next(); // 127.0.0.1:80 41 | /// it.next(); // 192.168.0.1:80 42 | /// it.next(); // 127.0.0.1:443 43 | /// it.next(); // 192.168.0.1:443 44 | /// it.next(); // None 45 | fn next(&mut self) -> Option { 46 | match self.product_it.next() { 47 | None => None, 48 | Some((port, ip)) => Some(SocketAddr::new(*ip, *port)), 49 | } 50 | } 51 | } 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | use super::SocketIterator; 56 | use std::net::{IpAddr, SocketAddr}; 57 | 58 | #[test] 59 | fn goes_through_every_ip_port_combination() { 60 | let addrs = vec![ 61 | "127.0.0.1".parse::().unwrap(), 62 | "192.168.0.1".parse::().unwrap(), 63 | ]; 64 | let ports: Vec = vec![22, 80, 443]; 65 | let mut it = SocketIterator::new(&addrs, &ports); 66 | 67 | assert_eq!(Some(SocketAddr::new(addrs[0], ports[0])), it.next()); 68 | assert_eq!(Some(SocketAddr::new(addrs[1], ports[0])), it.next()); 69 | assert_eq!(Some(SocketAddr::new(addrs[0], ports[1])), it.next()); 70 | assert_eq!(Some(SocketAddr::new(addrs[1], ports[1])), it.next()); 71 | assert_eq!(Some(SocketAddr::new(addrs[0], ports[2])), it.next()); 72 | assert_eq!(Some(SocketAddr::new(addrs[1], ports[2])), it.next()); 73 | assert_eq!(None, it.next()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/scripts/mod.rs: -------------------------------------------------------------------------------- 1 | //! Scripting engine to run scripts based on tags. 2 | //! This module serves to filter and run the scripts selected by the user. 3 | //! 4 | //! A new commandline and configuration file option was added. 5 | //! 6 | //! --scripts 7 | //! 8 | //! default 9 | //! This is the default behavior, like as it was from the beginning of RustScan. 10 | //! The user do not have to chose anything for this. This is the only script embedded in RustScan running as default. 11 | //! 12 | //! none 13 | //! The user have to use the --scripts none commandline argument or scripts = "none" in the config file. 14 | //! None of the scripts will run, this replaces the removed --no-nmap option. 15 | //! 16 | //! custom 17 | //! The user have to use the --scripts custom commandline argument or scripts = "custom" in the config file. 18 | //! Rustscan will look for the script configuration file in the user's home dir: home_dir/.rustscan_scripts.toml 19 | //! The config file have 3 optional fields, tag, developer and port. Just the tag field will be used forther in the process. 20 | //! RustScan will also look for available scripts in the user's home dir: home_dir/.rustscan_scripts 21 | //! and will try to read all the files, and parse them into a vector of ScriptFiles. 22 | //! Filtering on tags means the tags found in the rustscan_scripts.toml file will also have to be present in the Scriptfile, 23 | //! otherwise the script will not be selected. 24 | //! All of the rustscan_script.toml tags have to be present at minimum in a Scriptfile to get selected, but can be also more. 25 | //! 26 | //! Config file example: 27 | //! fixtures/test_rustscan_scripts.toml 28 | //! 29 | //! Script file examples: 30 | //! fixtures/test_script.py 31 | //! fixtures/test_script.pl 32 | //! fixtures/test_script.sh 33 | //! fixtures/test_script.txt 34 | //! 35 | //! call_format in script files can be of 2 variants. 36 | //! One is where all of the possible tags {{script}} {{ip}} {{port}} are there. 37 | //! The {{script}} part will be replaced with the scriptfile full path gathered while parsing available scripts. 38 | //! The {{ip}} part will be replaced with the ip we got from the scan. 39 | //! The {{port}} part will be reaplced with the ports separated with the ports_separator found in the script file 40 | //! 41 | //! And when there is only {{ip}} and {{port}} is in the format, ony those will be replaced with the arguments from the scan. 42 | //! This makes it easy to run a system installed command like nmap, and give any kind of arguments to it. 43 | //! 44 | //! 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. 45 | 46 | #![allow(clippy::module_name_repetitions)] 47 | 48 | use crate::input::ScriptsRequired; 49 | use anyhow::{anyhow, Result}; 50 | use serde_derive::{Deserialize, Serialize}; 51 | use std::collections::HashSet; 52 | use std::convert::TryInto; 53 | use std::fs::{self, File}; 54 | use std::io::{self, prelude::*}; 55 | use std::net::IpAddr; 56 | use std::path::PathBuf; 57 | use std::string::ToString; 58 | use subprocess::{Exec, ExitStatus}; 59 | use text_placeholder::Template; 60 | 61 | static DEFAULT: &str = r#"tags = ["core_approved", "RustScan", "default"] 62 | developer = [ "RustScan", "https://github.com/RustScan" ] 63 | ports_separator = "," 64 | call_format = "nmap -vvv -p {{port}} {{ip}}" 65 | "#; 66 | 67 | #[cfg(not(tarpaulin_include))] 68 | pub fn init_scripts(scripts: ScriptsRequired) -> Result> { 69 | let mut scripts_to_run: Vec = Vec::new(); 70 | 71 | match scripts { 72 | ScriptsRequired::None => Ok(scripts_to_run), 73 | ScriptsRequired::Default => { 74 | let default_script = 75 | toml::from_str::(&DEFAULT).expect("Failed to parse Script file."); 76 | scripts_to_run.push(default_script); 77 | Ok(scripts_to_run) 78 | } 79 | ScriptsRequired::Custom => { 80 | let scripts_dir_base = match dirs::home_dir() { 81 | Some(dir) => dir, 82 | None => return Err(anyhow!("Could not infer scripts path.")), 83 | }; 84 | let script_paths = match find_scripts(scripts_dir_base) { 85 | Ok(script_paths) => script_paths, 86 | Err(e) => return Err(anyhow!(e)), 87 | }; 88 | debug!("Scripts paths \n{:?}", script_paths); 89 | 90 | let parsed_scripts = parse_scripts(script_paths); 91 | debug!("Scripts parsed \n{:?}", parsed_scripts); 92 | 93 | let script_config = match ScriptConfig::read_config() { 94 | Ok(script_config) => script_config, 95 | Err(e) => return Err(anyhow!(e)), 96 | }; 97 | debug!("Script config \n{:?}", script_config); 98 | 99 | // Only Scripts that contain all the tags found in ScriptConfig will be selected. 100 | if script_config.tags.is_some() { 101 | let config_hashset: HashSet = 102 | script_config.tags.unwrap().into_iter().collect(); 103 | for script in &parsed_scripts { 104 | if script.tags.is_some() { 105 | let script_hashset: HashSet = 106 | script.tags.clone().unwrap().into_iter().collect(); 107 | if config_hashset.is_subset(&script_hashset) { 108 | scripts_to_run.push(script.to_owned()); 109 | } else { 110 | debug!( 111 | "\nScript tags does not match config tags {:?} {}", 112 | &script_hashset, 113 | script.path.clone().unwrap().display() 114 | ); 115 | } 116 | } 117 | } 118 | } 119 | debug!("\nScript(s) to run {:?}", scripts_to_run); 120 | Ok(scripts_to_run) 121 | } 122 | } 123 | } 124 | 125 | pub fn parse_scripts(scripts: Vec) -> Vec { 126 | let mut parsed_scripts: Vec = Vec::with_capacity(scripts.len()); 127 | for script in scripts { 128 | debug!("Parsing script {}", &script.display()); 129 | if let Some(script_file) = ScriptFile::new(script) { 130 | parsed_scripts.push(script_file); 131 | } 132 | } 133 | parsed_scripts 134 | } 135 | 136 | #[derive(Clone, Debug)] 137 | pub struct Script { 138 | // Path to the script itself. 139 | path: Option, 140 | 141 | // Ip got from scanner. 142 | ip: IpAddr, 143 | 144 | // Ports found with portscan. 145 | open_ports: Vec, 146 | 147 | // Port found in ScriptFile, if defined only this will run with the ip. 148 | trigger_port: Option, 149 | 150 | // Character to join ports in case we want to use a string format of them, for example nmap -p. 151 | ports_separator: Option, 152 | 153 | // Tags found in ScriptFile. 154 | tags: Option>, 155 | 156 | // The format how we want the script to run. 157 | call_format: Option, 158 | } 159 | 160 | #[derive(Serialize)] 161 | struct ExecPartsScript { 162 | script: String, 163 | ip: String, 164 | port: String, 165 | } 166 | 167 | #[derive(Serialize)] 168 | struct ExecParts { 169 | ip: String, 170 | port: String, 171 | } 172 | 173 | impl Script { 174 | pub fn build( 175 | path: Option, 176 | ip: IpAddr, 177 | open_ports: Vec, 178 | trigger_port: Option, 179 | ports_separator: Option, 180 | tags: Option>, 181 | call_format: Option, 182 | ) -> Self { 183 | Self { 184 | path, 185 | ip, 186 | open_ports, 187 | trigger_port, 188 | ports_separator, 189 | tags, 190 | call_format, 191 | } 192 | } 193 | 194 | // Some variables get changed before read, and compiler throws warning on warn(unused_assignments) 195 | #[allow(unused_assignments)] 196 | pub fn run(self) -> Result { 197 | debug!("run self {:?}", &self); 198 | 199 | let separator = self.ports_separator.unwrap_or_else(|| ",".into()); 200 | 201 | let mut ports_str = self 202 | .open_ports 203 | .iter() 204 | .map(ToString::to_string) 205 | .collect::>() 206 | .join(&separator); 207 | if let Some(port) = self.trigger_port { 208 | ports_str = port; 209 | } 210 | 211 | let mut final_call_format = String::new(); 212 | if let Some(call_format) = self.call_format { 213 | final_call_format = call_format; 214 | } else { 215 | return Err(anyhow!("Failed to parse execution format.")); 216 | } 217 | let default_template: Template = Template::new(&final_call_format); 218 | let mut to_run = String::new(); 219 | 220 | if final_call_format.contains("{{script}}") { 221 | let exec_parts_script: ExecPartsScript = ExecPartsScript { 222 | script: self.path.unwrap().to_str().unwrap().to_string(), 223 | ip: self.ip.to_string(), 224 | port: ports_str, 225 | }; 226 | to_run = default_template.fill_with_struct(&exec_parts_script)?; 227 | } else { 228 | let exec_parts: ExecParts = ExecParts { 229 | ip: self.ip.to_string(), 230 | port: ports_str, 231 | }; 232 | to_run = default_template.fill_with_struct(&exec_parts)?; 233 | } 234 | debug!("\nScript format to run {}", to_run); 235 | 236 | let arguments = shell_words::split(&to_run).expect("Failed to parse script arguments"); 237 | 238 | execute_script(arguments) 239 | } 240 | } 241 | 242 | #[cfg(not(tarpaulin_include))] 243 | fn execute_script(mut arguments: Vec) -> Result { 244 | debug!("\nScript arguments vec: {:?}", &arguments); 245 | let process = Exec::cmd(&arguments.remove(0)).args(&arguments); 246 | match process.capture() { 247 | Ok(c) => { 248 | let es = match c.exit_status { 249 | ExitStatus::Exited(c) => c.try_into().unwrap(), 250 | ExitStatus::Signaled(c) => c.into(), 251 | ExitStatus::Other(c) => c, 252 | ExitStatus::Undetermined => -1, 253 | }; 254 | if es != 0 { 255 | return Err(anyhow!("Exit code = {}", es)); 256 | } 257 | Ok(c.stdout_str()) 258 | } 259 | Err(error) => { 260 | debug!("Command error {}", error.to_string()); 261 | Err(anyhow!(error.to_string())) 262 | } 263 | } 264 | } 265 | 266 | pub fn find_scripts(mut path: PathBuf) -> Result> { 267 | path.push(".rustscan_scripts"); 268 | if path.is_dir() { 269 | debug!("Scripts folder found {}", &path.display()); 270 | let mut files_vec: Vec = Vec::new(); 271 | for entry in fs::read_dir(path)? { 272 | let entry = entry?; 273 | files_vec.push(entry.path()); 274 | } 275 | Ok(files_vec) 276 | } else { 277 | Err(anyhow!("Can't find scripts folder {}", path.display())) 278 | } 279 | } 280 | 281 | #[derive(Debug, Clone, Deserialize)] 282 | pub struct ScriptFile { 283 | pub path: Option, 284 | pub tags: Option>, 285 | pub developer: Option>, 286 | pub port: Option, 287 | pub ports_separator: Option, 288 | pub call_format: Option, 289 | } 290 | 291 | impl ScriptFile { 292 | fn new(script: PathBuf) -> Option { 293 | let real_path = script.clone(); 294 | let mut lines_buf = String::new(); 295 | if let Ok(file) = File::open(script) { 296 | for line in io::BufReader::new(file).lines().skip(1) { 297 | if let Ok(mut line) = line { 298 | if line.starts_with('#') { 299 | line.retain(|c| c != '#'); 300 | line = line.trim().to_string(); 301 | line.push('\n'); 302 | lines_buf.push_str(&line); 303 | } else { 304 | break; 305 | } 306 | } 307 | } 308 | } else { 309 | debug!("Failed to read file: {}", &real_path.display()); 310 | return None; 311 | } 312 | debug!("ScriptFile {} lines\n{}", &real_path.display(), &lines_buf); 313 | 314 | match toml::from_str::(&lines_buf) { 315 | Ok(mut parsed) => { 316 | debug!("Parsed ScriptFile{} \n{:?}", &real_path.display(), &parsed); 317 | parsed.path = Some(real_path); 318 | // parsed_scripts.push(parsed); 319 | Some(parsed) 320 | } 321 | Err(e) => { 322 | debug!("Failed to parse ScriptFile headers {}", e.to_string()); 323 | None 324 | } 325 | } 326 | } 327 | } 328 | 329 | #[derive(Debug, Deserialize, Clone)] 330 | pub struct ScriptConfig { 331 | pub tags: Option>, 332 | pub ports: Option>, 333 | pub developer: Option>, 334 | } 335 | 336 | #[cfg(not(tarpaulin_include))] 337 | impl ScriptConfig { 338 | pub fn read_config() -> Result { 339 | let mut home_dir = match dirs::home_dir() { 340 | Some(dir) => dir, 341 | None => return Err(anyhow!("Could not infer ScriptConfig path.")), 342 | }; 343 | home_dir.push(".rustscan_scripts.toml"); 344 | 345 | let content = fs::read_to_string(home_dir)?; 346 | let config = toml::from_str::(&content)?; 347 | Ok(config) 348 | } 349 | } 350 | 351 | #[cfg(test)] 352 | mod tests { 353 | use super::{find_scripts, parse_scripts, Script, ScriptFile}; 354 | 355 | // Function for testing only, it inserts static values into ip and open_ports 356 | // Doesn't use impl in case it's implemented in the super module at some point 357 | fn into_script(script_f: ScriptFile) -> Script { 358 | Script::build( 359 | script_f.path, 360 | "127.0.0.1".parse().unwrap(), 361 | vec![80, 8080], 362 | script_f.port, 363 | script_f.ports_separator, 364 | script_f.tags, 365 | script_f.call_format, 366 | ) 367 | } 368 | 369 | #[test] 370 | fn find_and_parse_scripts() { 371 | let scripts = find_scripts("fixtures/".into()).unwrap(); 372 | let scripts = parse_scripts(scripts); 373 | assert_eq!(scripts.len(), 4); 374 | } 375 | 376 | #[test] 377 | #[should_panic] 378 | fn find_invalid_folder() { 379 | let _scripts = find_scripts("Cargo.toml".into()).unwrap(); 380 | } 381 | 382 | #[test] 383 | #[should_panic] 384 | fn open_script_file_invalid_headers() { 385 | ScriptFile::new("fixtures/.rustscan_scripts/test_script_invalid_headers.txt".into()) 386 | .unwrap(); 387 | } 388 | 389 | #[test] 390 | #[should_panic] 391 | fn open_script_file_invalid_call_format() { 392 | let mut script_f = 393 | ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap(); 394 | script_f.call_format = Some("qwertyuiop".to_string()); 395 | let script: Script = into_script(script_f); 396 | let _output = script.run().unwrap(); 397 | } 398 | 399 | #[test] 400 | #[should_panic] 401 | fn open_script_file_missing_call_format() { 402 | let mut script_f = 403 | ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap(); 404 | script_f.call_format = None; 405 | let script: Script = into_script(script_f); 406 | let _output = script.run().unwrap(); 407 | } 408 | 409 | #[test] 410 | #[should_panic] 411 | fn open_nonexisting_script_file() { 412 | ScriptFile::new("qwertyuiop.txt".into()).unwrap(); 413 | } 414 | 415 | #[test] 416 | fn parse_txt_script() { 417 | let script_f = 418 | ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap(); 419 | assert_eq!( 420 | script_f.tags, 421 | Some(vec!["core_approved".to_string(), "example".to_string()]) 422 | ); 423 | assert_eq!( 424 | script_f.developer, 425 | Some(vec![ 426 | "example".to_string(), 427 | "https://example.org".to_string() 428 | ]) 429 | ); 430 | assert_eq!(script_f.ports_separator, Some(",".to_string())); 431 | assert_eq!( 432 | script_f.call_format, 433 | Some("nmap -vvv -p {{port}} {{ip}}".to_string()) 434 | ); 435 | } 436 | 437 | #[test] 438 | fn run_bash_script() { 439 | let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.sh".into()).unwrap(); 440 | let script: Script = into_script(script_f); 441 | let output = script.run().unwrap(); 442 | // output has a newline at the end by default, .trim() trims it 443 | assert_eq!(output.trim(), "127.0.0.1 80,8080"); 444 | } 445 | 446 | #[test] 447 | fn run_python_script() { 448 | let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.py".into()).unwrap(); 449 | let script: Script = into_script(script_f); 450 | let output = script.run().unwrap(); 451 | // output has a newline at the end by default, .trim() trims it 452 | assert_eq!( 453 | output.trim(), 454 | "Python script ran with arguments ['fixtures/.rustscan_scripts/test_script.py', '127.0.0.1', '80,8080']" 455 | ); 456 | } 457 | 458 | #[test] 459 | fn run_perl_script() { 460 | let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.pl".into()).unwrap(); 461 | let script: Script = into_script(script_f); 462 | let output = script.run().unwrap(); 463 | // output has a newline at the end by default, .trim() trims it 464 | 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"); 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /src/tui.rs: -------------------------------------------------------------------------------- 1 | /// Terminal User Interface Module for RustScan 2 | /// Defines macros to use 3 | 4 | #[macro_export] 5 | macro_rules! warning { 6 | ($name:expr) => { 7 | use ansi_term::Colour::Red; 8 | println!("{} {}", Red.bold().paint("[!]"), $name); 9 | }; 10 | ($name:expr, $greppable:expr, $accessible:expr) => { 11 | use ansi_term::Colour::Red; 12 | // if not greppable then print, otherwise no else statement so do not print. 13 | if !$greppable { 14 | if $accessible { 15 | // Don't print the ascii art 16 | println!("{}", $name); 17 | } else { 18 | println!("{} {}", Red.bold().paint("[!]"), $name); 19 | } 20 | } 21 | }; 22 | } 23 | 24 | #[macro_export] 25 | macro_rules! detail { 26 | ($name:expr) => { 27 | use ansi_term::Colour::Blue; 28 | println!("{} {}", Blue.bold().paint("[~]"), $name); 29 | }; 30 | ($name:expr, $greppable:expr, $accessible:expr) => { 31 | use ansi_term::Colour::Blue; 32 | // if not greppable then print, otherwise no else statement so do not print. 33 | if !$greppable { 34 | if $accessible { 35 | // Don't print the ascii art 36 | println!("{}", $name); 37 | } else { 38 | println!("{} {}", Blue.bold().paint("[~]"), $name); 39 | } 40 | } 41 | }; 42 | } 43 | 44 | #[macro_export] 45 | macro_rules! output { 46 | ($name:expr) => { 47 | use ansi_term::Colour::RGB; 48 | println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name); 49 | }; 50 | ($name:expr, $greppable:expr, $accessible:expr) => { 51 | use ansi_term::Colour::RGB; 52 | // if not greppable then print, otherwise no else statement so do not print. 53 | if !$greppable { 54 | if $accessible { 55 | // Don't print the ascii art 56 | println!("{}", $name); 57 | } else { 58 | println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name); 59 | } 60 | } 61 | }; 62 | } 63 | 64 | #[macro_export] 65 | macro_rules! funny_opening { 66 | // prints a funny quote / opening 67 | () => { 68 | use rand::seq::SliceRandom; 69 | let quotes = vec![ 70 | "Nmap? More like slowmap.🐢", 71 | "🌍HACK THE PLANET🌍", 72 | "Real hackers hack time ⌛", 73 | "Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan", 74 | "😵 https://admin.tryhackme.com", 75 | "0day was here ♥", 76 | ]; 77 | let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap(); 78 | 79 | println!("{}\n", random_quote); 80 | // println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name); 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /tests/timelimits.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Test rustscan against different targets with a time limit. 3 | * The tests assumes target/debug/rustscan has already been built. 4 | * 5 | * The tests are #[ignore] to avoid running them during normal development. 6 | * 7 | * Their tests in the timelimits module are run by travis during CI. 8 | */ 9 | 10 | use std::process::Command; 11 | use std::time::Duration; 12 | use wait_timeout::ChildExt; 13 | 14 | const TIMEOUT_MARGIN: u32 = 3; 15 | 16 | #[cfg(not(tarpaulin_include))] 17 | fn run_rustscan_with_timeout(args: &[&str], timeout: Duration) { 18 | println!("Running: target/debug/rustscan: {}", args.join(" ")); 19 | 20 | use std::time::Instant; 21 | 22 | let start = Instant::now(); 23 | 24 | let mut child = Command::new("target/debug/rustscan") 25 | .args(args) 26 | .spawn() 27 | .unwrap(); 28 | 29 | let mut tries = TIMEOUT_MARGIN; 30 | loop { 31 | match child.wait_timeout(timeout).unwrap() { 32 | Some(_status) => break, 33 | None => { 34 | tries -= 1; 35 | if tries == 0 { 36 | // child hasn't exited yet 37 | child.kill().unwrap(); 38 | child.wait().unwrap().code(); 39 | panic!("Timeout while running command"); 40 | } 41 | } 42 | } 43 | } 44 | let end = Instant::now(); 45 | let duration = end.saturating_duration_since(start).as_secs_f32(); 46 | 47 | println!("time: {:1.1}s", duration); 48 | } 49 | 50 | mod timelimits { 51 | 52 | #[test] 53 | #[ignore] 54 | fn scan_localhost() { 55 | let timeout = super::Duration::from_secs(25); 56 | super::run_rustscan_with_timeout(&["--greppable", "--no-nmap", "127.0.0.1"], timeout); 57 | } 58 | 59 | #[test] 60 | #[ignore] 61 | fn scan_google_com() { 62 | super::run_rustscan_with_timeout( 63 | &[ 64 | "--greppable", 65 | "--no-nmap", 66 | "-u", 67 | "5000", 68 | "-b", 69 | "2500", 70 | "google.com", 71 | ], 72 | super::Duration::from_secs(28), 73 | ); 74 | } 75 | 76 | #[test] 77 | #[ignore] 78 | fn scan_example_com() { 79 | super::run_rustscan_with_timeout( 80 | &[ 81 | "--greppable", 82 | "--no-nmap", 83 | "-u", 84 | "5000", 85 | "-b", 86 | "2500", 87 | "example.com", 88 | ], 89 | super::Duration::from_secs(28), 90 | ); 91 | } 92 | 93 | #[test] 94 | #[ignore] 95 | fn scan_rustscan_cmnatic_co_uk() { 96 | super::run_rustscan_with_timeout( 97 | &[ 98 | "--greppable", 99 | "--no-nmap", 100 | "-u", 101 | "5000", 102 | "-b", 103 | "2500", 104 | "rustscan.cmnatic.co.uk", 105 | ], 106 | super::Duration::from_secs(26), 107 | ); 108 | } 109 | } 110 | --------------------------------------------------------------------------------