Compare commits

...
Author SHA1 Message Date
Bernardo Chaves b490eb08be Refactor RangeIterator to not require Option types
There's no need to deal with `unwrap` for this specific algorithm,
instead we can rely on simple types to achieve the same result.
2020-08-08 14:08:43 -04:00
Brandon 990e92dcbf Merge pull request #134 from RustScan/bee-add-a11y-mode
Add a11y mode
2020-08-08 13:45:59 +01:00
Brandon 883e89fe5e cargo fmt 2020-08-08 13:26:45 +01:00
Brandon 1b15cc2f71 Merge branch 'master' into bee-add-a11y-mode 2020-08-08 13:24:41 +01:00
Brandon 18b574f6bb Merge pull request #137 from Nugine/fix-typo
Fix typo of "ulimt"
2020-08-08 13:22:12 +01:00
Nugine 3fafee42b9 Fix typo of "ulimt" 2020-08-08 19:54:12 +08:00
Brandon 8b21dfd9a2 Merge pull request #135 from RustScan/ba-add-scan-strategy
Add port scanning strategy
2020-08-08 00:43:07 +01:00
Bernardo Chaves 2f16729c54 Add port scanning strategy
User will be able to pick from:
  - serial: in order to have their ports scanned in ascending order
  - random: in order to have their ports scanned randomly
2020-08-07 18:59:44 -04:00
Brandon 785c2a4de1 fixed tests 2020-08-07 22:28:57 +01:00
Brandon 80cc97bf83 removed perf data 2020-08-07 22:04:33 +01:00
Brandon 7a825dae41 the printopening() would suck in a TTS program 2020-08-07 21:39:53 +01:00
Brandon 751c4daff6 1.7.1 2020-08-07 17:48:37 +01:00
Brandon f6214cfb27 Merge branch 'master' of github.com:brandonskerritt/RustScan 2020-08-07 17:48:10 +01:00
Brandon a92eb80365 updated lock files 2020-08-07 17:30:39 +01:00
Brandon aeab4fb68c Merge pull request #131 from RustScan/bee-colours
Added TUI A11Y colours + Quiet Mode Macros
2020-08-07 17:25:57 +01:00
Brandon 9ab15c9d8b cargo fmt 2020-08-07 17:11:14 +01:00
Brandon 7146c23ed9 cleanup 2020-08-07 17:06:43 +01:00
Brandon f37f2d8f9d switching to master 2020-08-07 16:50:42 +01:00
Brandon 5a3a4d1c0c cargo fmt 2020-08-07 16:36:14 +01:00
Brandon 33e7cd44b6 added quiet mode to macros 2020-08-07 16:33:25 +01:00
Brandon 8288856949 added quotes 2020-08-07 13:21:19 +01:00
Brandon c3b96e262d Merge pull request #130 from RustScan/bee-add-TUI-module
Bee add tui module
2020-08-07 12:53:46 +01:00
Brandon a54868000e cargo format 2020-08-07 12:37:05 +01:00
Brandon 8f4acdf99a cleaned up colours 2020-08-07 12:33:44 +01:00
Brandon c637042906 fixed visual table 2020-08-07 02:08:12 +01:00
Brandon 766164588c fixed spacing 2020-08-07 01:53:14 +01:00
Brandon b2be83c04a added basic TUI interface 2020-08-07 00:52:56 +01:00
Brandon a3654b68eb added basic TUI interface 2020-08-07 00:40:28 +01:00
Brandon 1873a92951 Merge pull request #128 from RustScan/dependabot/cargo/structopt-0.3.16
Bump structopt from 0.3.15 to 0.3.16
2020-08-06 11:25:24 +01:00
dependabot-preview[bot] c7a422b962 Bump structopt from 0.3.15 to 0.3.16
Bumps [structopt](https://github.com/TeXitoi/structopt) from 0.3.15 to 0.3.16.
- [Release notes](https://github.com/TeXitoi/structopt/releases)
- [Changelog](https://github.com/TeXitoi/structopt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TeXitoi/structopt/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>
2020-08-06 08:39:30 +00:00
Bernardo Chaves 0eec9233bd Make use of our new RangeIterator
We now rely on a pseudo random generator to scan our IPs. We also
interleave IP scanning in order to not overwhelm a single IP.
2020-08-06 01:36:36 +01:00
Bernardo Chaves 0f18bb49a3 Add pseudo random port scanning
This is based on the Linear Congruential Generator algorithm which
guarantees we will iterate through an entire range in a pseudo random
manner.

This implementation should be bound in memory by the batch_size
specified by the user, which will be in average 1_000.
2020-08-06 01:36:36 +01:00
yogendra0sharma e546608ff8 show batch_size to user 2020-08-05 19:12:53 +01:00
yogendra0sharma a7d1952b72 Fix Issue Unneccesary text printed 2020-08-05 19:12:53 +01:00
Brandon 67726cc490 1.6 2020-08-04 23:30:52 +01:00
Brandon d5d39cacd3 Merge pull request #117 from RustScan/ba-implement-multi-ip
Support multiple IPs
2020-08-04 23:21:17 +01:00
Bernardo Chaves 52cbbeeac6 Adjust messages taking into account quiet mode 2020-08-04 17:40:23 -04:00
Bernardo Chaves 23c049413b Support multiple IPs
IPs should be provided in a comma separated list.

Right now every IP will be scanned sequentially and NMAP will run before
the next IP scan starts. This will change on the long term once we
provide randomized scans in order to not overwhelm hosts or trigger
detection systems in place.
2020-08-04 16:17:27 -04:00
9 changed files with 716 additions and 347 deletions

No files matched your search

Generated
+87 -15
View File
@@ -18,6 +18,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "arrayref"
version = "0.3.6"
@@ -156,11 +165,11 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap"
version = "2.33.1"
version = "2.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48"
dependencies = [
"ansi_term",
"ansi_term 0.11.0",
"atty",
"bitflags",
"strsim",
@@ -181,10 +190,16 @@ dependencies = [
]
[[package]]
name = "concurrent-queue"
version = "1.2.0"
name = "colorful"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e296417c8154304ac70aceda41f05318f986f7c0c767bcb0a2366fbb890e78e1"
checksum = "0bca1619ff57dd7a56b58a8e25ef4199f123e78e503fe1653410350a1b98ae65"
[[package]]
name = "concurrent-queue"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
dependencies = [
"cache-padded",
]
@@ -241,9 +256,9 @@ dependencies = [
[[package]]
name = "event-listener"
version = "2.3.0"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298f00c3b04c1d9b4cb86aefaaa35348af0957d98b30a5306fc635f8e718923d"
checksum = "68082183f458867ce7cddea16d4df4443c1537112c0c09c450dedc09daf5c719"
[[package]]
name = "fastrand"
@@ -361,6 +376,12 @@ dependencies = [
"slab",
]
[[package]]
name = "gcd"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c7cd301bf2ab11ae4e5bdfd79c221d97a25e46c089144a62ee9d09cb32d2b92"
[[package]]
name = "getrandom"
version = "0.1.14"
@@ -498,6 +519,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@@ -558,6 +585,47 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
@@ -617,14 +685,18 @@ dependencies = [
[[package]]
name = "rustscan"
version = "1.5.2"
version = "1.7.1"
dependencies = [
"ansi_term 0.12.1",
"async-std",
"colored",
"colorful",
"dirs",
"env_logger",
"futures",
"gcd",
"log",
"rand",
"rlimit",
"shell-words",
"structopt",
@@ -689,9 +761,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.15"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c"
checksum = "de5472fb24d7e80ae84a7801b7978f95a19ec32cb1876faea59ab711eb901976"
dependencies = [
"clap",
"lazy_static",
@@ -700,9 +772,9 @@ dependencies = [
[[package]]
name = "structopt-derive"
version = "0.4.8"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118"
checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99"
dependencies = [
"heck",
"proc-macro-error",
@@ -713,9 +785,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.37"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239f255b9e3429350f188c27b807fc9920a15eb9145230ff1a7d054c08fec319"
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
dependencies = [
"proc-macro2",
"quote",
+6 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "rustscan"
version = "1.5.2"
version = "1.7.1"
authors = ["Bee <[email protected]>"]
edition = "2018"
description = "Faster Nmap Scanning with Rust"
@@ -15,7 +15,7 @@ readme="README.md"
[dependencies]
colored = "2.0.0"
structopt = "0.3.15"
structopt = "0.3.16"
async-std = "1.6.2"
futures = "0.3"
rlimit = "0.3.0"
@@ -23,6 +23,10 @@ shell-words = "1.0.0"
log = "0.4.0"
env_logger = "0.7.1"
dirs = "3.0.1"
gcd = "2.0.1"
rand = "0.7.3"
colorful = "0.2.1"
ansi_term = "0.12.1"
[package.metadata.deb]
depends = "$auto, nmap"
+3 -3
View File
@@ -29,7 +29,7 @@
<hr>
# 🤔 What is this?
1. Find ports quickly using Rustscan (**8 seconds at its fastest**).
1. Find ports quickly using Rustscan (**3 seconds at its fastest**).
2. Automatically runs `nmap` on those ports.
3. ???
4. Profit!
@@ -52,7 +52,7 @@
</tr>
</table>
<sub><b>Note</b> This is an older gif. RustScan's current top speed is 8 seconds for all 65k ports. This gif is 26 seconds.</sub><br>
<sub><b>Note</b> This is an older gif. RustScan's current top speed is 3 seconds for all 65k ports. This gif is 26 seconds.</sub><br>
RustScans **only** job is to reduce the friction between finding open ports and inputting them into nmap.
@@ -281,7 +281,7 @@ In this case, I would say it is safe to increase the open file limit. As most Li
Although, if this breaks anything, please don't blame me.
**Windows Subsystem for Linux**
Windows Subsystem for Linux does not support ulimt (see issue #39).
Windows Subsystem for Linux does not support ulimit (see issue #39).
The best way is to use it on a host computer, in Docker, or in a VM that isn't WSL.
+160 -188
View File
@@ -1,17 +1,28 @@
extern crate shell_words;
mod tui;
mod scanner;
use scanner::Scanner;
use colored::*;
mod port_strategy;
use port_strategy::PortStrategy;
use colorful::Color;
use colorful::Colorful;
use futures::executor::block_on;
use rlimit::Resource;
use rlimit::{getrlimit, setrlimit};
use std::process::{exit, Command};
use std::collections::HashMap;
use std::process::Command;
use std::{net::IpAddr, time::Duration};
use structopt::StructOpt;
use structopt::{clap::arg_enum, StructOpt};
extern crate colorful;
extern crate dirs;
const LOWEST_PORT_NUMBER: u16 = 1;
const TOP_PORT_NUMBER: u16 = 65535;
// Average value for Ubuntu
const DEFAULT_FILE_DESCRIPTORS_LIMIT: rlimit::rlim = 8000;
// Safest batch size based on experimentation
@@ -20,6 +31,14 @@ const AVERAGE_BATCH_SIZE: rlimit::rlim = 3000;
#[macro_use]
extern crate log;
arg_enum! {
#[derive(Debug, StructOpt)]
pub enum ScanOrder {
Serial,
Random,
}
}
#[derive(StructOpt, Debug)]
#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
/// Fast Port Scanner built in Rust.
@@ -28,20 +47,24 @@ extern crate log;
/// - Discord https://discord.gg/GFrQsGy
/// - GitHub https://github.com/RustScan/RustScan
struct Opts {
/// The IP address to scan
#[structopt(parse(try_from_str))]
ip: IpAddr,
/// A list of comma separated IP addresses to be scanned.
#[structopt(use_delimiter = true, parse(try_from_str), required = true)]
ips: Vec<IpAddr>,
///Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
#[structopt(short, long)]
quiet: bool,
//Accessible mode. Turns off features which negatively affect screen readers.
#[structopt(short, long)]
accessible: bool,
/// The batch size for port scanning, it increases or slows the speed of
/// scanning. Depends on the open file limit of your OS. If you do 65535
/// it will do every port at the same time. Although, your OS may not
/// support this.
#[structopt(short, long, default_value = "4500")]
batch_size: u32,
batch_size: u16,
/// The timeout in milliseconds before a port is assumed to be closed.
#[structopt(short, long, default_value = "1500")]
@@ -51,11 +74,18 @@ struct Opts {
#[structopt(short, long)]
ulimit: Option<rlimit::rlim>,
/// The order of scanning to be performed. The "serial" option will
/// scan ports in ascending order while the "random" option will scan
/// ports randomly.
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
scan_order: ScanOrder,
/// The Nmap arguments to run.
/// To use the argument -A, end RustScan's args with '-- -A'.
/// Example: 'rustscan -T 1500 127.0.0.1 -- -A -sC'.
/// This command adds -Pn -vvv -p $PORTS automatically to nmap.
/// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"")
#[structopt(last = true)]
command: Vec<String>,
}
@@ -63,91 +93,105 @@ struct Opts {
/// Faster Nmap scanning with Rust
/// If you're looking for the actual scanning, check out the module Scanner
fn main() {
// logger
env_logger::init();
info!("Starting up");
let opts = Opts::from_args();
info!("Mains() `opts` arguments are {:?}", opts);
if !opts.quiet {
if !opts.quiet && !opts.accessible {
print_opening();
}
let ulimit: rlimit::rlim = adjust_ulimit_size(&opts);
let batch_size: u32 = infer_batch_size(&opts, ulimit);
let batch_size: u16 = infer_batch_size(&opts, ulimit);
// 65535 + 1 because of 0 indexing
let scanner = Scanner::new(
opts.ip,
1,
65535,
&opts.ips,
batch_size,
Duration::from_millis(opts.timeout.into()),
opts.quiet,
PortStrategy::pick(LOWEST_PORT_NUMBER, TOP_PORT_NUMBER, opts.scan_order),
);
let scan_result = block_on(scanner.run());
let mut ports_per_ip = HashMap::new();
// prints ports and places them into nmap string
let nmap_str_ports: Vec<String> = scan_result
.into_iter()
.map(|port| port.to_string())
.collect();
for socket in scan_result {
ports_per_ip
.entry(socket.ip())
.or_insert_with(Vec::new)
.push(socket.port());
}
// if no ports are found, suggest running with less
if nmap_str_ports.is_empty() {
panic!("{} Looks like I didn't find any open ports. This is usually caused by a high batch size.
for ip in opts.ips {
if ports_per_ip.contains_key(&ip) {
continue;
}
// If we got here it means the IP was not found within the HashMap, this
// means the scan couldn't find any open ports for it.
let x = format!("{} Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size.
\n*I used {} batch size, consider lowering to {} with {} or a comfortable number for your system.
\n Alternatively, increase the timeout if your ping is high. Rustscan -T 2000 for 2000 second timeout.", "ERROR".red(),
\n Alternatively, increase the timeout if your ping is high. Rustscan -T 2000 for 2000 second timeout.\n",
"ERROR",
ip,
opts.batch_size,
(opts.batch_size / 2).to_string().green(),
"'rustscan -b <batch_size> <ip address>'".green());
(opts.batch_size / 2).to_string(),
"'rustscan -b <batch_size> <ip address>'");
warning!(x, opts.quiet);
}
// Tells the user we are now switching to Nmap
if !opts.quiet {
println!("{}", "Starting nmap.".blue(),);
for (ip, ports) in ports_per_ip.iter_mut() {
let nmap_str_ports: Vec<String> = ports.into_iter().map(|port| port.to_string()).collect();
detail!("Starting Nmap", opts.quiet);
// nmap port style is 80,443. Comma separated with no spaces.
let ports_str = nmap_str_ports.join(",");
// if quiet mode is on nmap should not be spawned
if opts.quiet {
println!("{}", ports_str);
continue;
}
let addr = ip.to_string();
let user_nmap_args =
shell_words::split(&opts.command.join(" ")).expect("failed to parse nmap arguments");
let nmap_args = build_nmap_arguments(&addr, &ports_str, &user_nmap_args, ip.is_ipv6());
output!(format!(
"The Nmap command to be run is nmap {}\n",
&nmap_args.join(" ")
));
// Runs the nmap command and spawns it as a process.
let mut child = Command::new("nmap")
.args(&nmap_args)
.spawn()
.expect("failed to execute nmap process");
child.wait().expect("failed to wait on nmap process");
}
// nmap port style is 80,443. Comma seperated with no spaces.
let ports_str = nmap_str_ports.join(",");
// if quiet mode is on, return ports and exit
if opts.quiet {
println!("{}", ports_str);
exit(1);
}
let addr = opts.ip.to_string();
let user_nmap_args =
shell_words::split(&opts.command.join(" ")).expect("failed to parse nmap arguments");
let nmap_args = build_nmap_arguments(&addr, &ports_str, &user_nmap_args, opts.ip.is_ipv6());
if !opts.quiet {
println!("The Nmap command to be run is {}", &nmap_args.join(" "));
}
// Runs the nmap command and spawns it as a process.
let mut child = Command::new("nmap")
.args(&nmap_args)
.spawn()
.expect("failed to execute nmap process");
child.wait().expect("failed to wait on nmap process");
}
/// Prints the opening title of RustScan
fn print_opening() {
info!("Printing opening");
let s = "
_____ _ _____
| __ \\ | | / ____|
| |__) | _ ___| |_| (___ ___ __ _ _ __
| _ / | | / __| __|\\___ \\ / __/ _` | '_ \\
| | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |
|_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|
Faster nmap scanning with rust.";
println!("{}\n", s.green());
let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
Faster Nmap scanning with Rust."#;
println!("{}", s.gradient(Color::Green).bold());
let info = r#"________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------"#;
println!("{}", info.gradient(Color::Yellow).bold());
funny_opening!();
let config_path = match dirs::config_dir() {
Some(mut path) => {
@@ -158,11 +202,10 @@ fn print_opening() {
None => panic!("Couldn't find config dir."),
};
println!(
"{} {:?}\n",
"The config file is expected to be at".yellow(),
config_path
);
detail!(format!(
"{} {:?}",
"The config file is expected to be at", config_path
));
}
#[cfg(not(tarpaulin_include))]
fn build_nmap_arguments<'a>(
@@ -192,11 +235,12 @@ fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
match setrlimit(Resource::NOFILE, limit, limit) {
Ok(_) => {
if !opts.quiet {
println!("\nAutomatically increasing ulimit value to {}.\n", limit);
}
detail!(
format!("Automatically increasing ulimit value to {}.", limit),
opts.quiet
);
}
Err(_) => println!("{}", "ERROR. Failed to set ulimit value.".red()),
Err(_) => println!("{}", "ERROR. Failed to set ulimit value."),
}
}
@@ -205,14 +249,14 @@ fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
rlim
}
fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u32 {
fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
let mut batch_size: rlimit::rlim = opts.batch_size.into();
// Adjust the batch size when the ulimit value is lower than the desired batch size
if ulimit < batch_size {
if !opts.quiet {
println!("{}", "WARNING: Your file description limit is lower than the provided batch size. Please considering upping this (instructions in our README). NOTE: this may be dangerous and may cause harm to sensitive servers. Automatically reducing the batch Size to match your system's limit, this process isn't harmful but reduces speed.".red());
}
warning!("File limit is lower than default batch size. Consider upping with --ulimt. May cause harm to sensitive servers",
opts.quiet
);
// When the OS supports high file limits like 8000, but the user
// selected a batch size higher than this we should reduce it to
@@ -221,8 +265,8 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u32 {
// ulimit is smaller than aveage batch size
// user must have very small ulimit
// decrease batch size to half of ulimit
warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. ");
info!("Halving batch_size because ulimit is smaller than average batch size");
println!("{}", "WARNING. Your open file description limit is smaller than expected. You can increase the ulimit with the '-u' flag like '-u 5000' to get default size. Or, use the Docker image. If you do not increase ulimit your RustScan speeds will be much slower in comparison to a normal ulimit.".red());
batch_size = ulimit / 2
} else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT {
info!("Batch size is now average batch size");
@@ -234,112 +278,31 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u32 {
// When the ulimit is higher than the batch size let the user know that the
// batch size can be increased unless they specified the ulimit themselves.
else if ulimit + 2 > batch_size && (opts.ulimit.is_none()) {
if !opts.quiet {
println!(
"Your file descriptor limit is higher than the batch size. You can potentially increase the speed by increasing the batch size, but this may cause harm to sensitive servers. Your limit is {}, try batch size {}.\n",
ulimit,
ulimit - 1
);
}
detail!(format!(
"File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.",
ulimit - 100
), opts.quiet);
}
println!("The batch size is {}", batch_size);
batch_size as u32
batch_size as u16
}
#[cfg(test)]
mod tests {
use super::Scanner;
use crate::{adjust_ulimit_size, build_nmap_arguments, infer_batch_size, print_opening, Opts};
use async_std::task::block_on;
use std::{net::IpAddr, str::FromStr, time::Duration};
use crate::{adjust_ulimit_size, infer_batch_size, print_opening, Opts, ScanOrder};
use std::{net::IpAddr, str::FromStr};
#[test]
fn scanner_runs() {
// Makes sure te program still runs and doesn't panic
let addr = match "127.0.0.1".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 65535, 100, Duration::from_millis(100), true);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn ipv6_scanner_runs() {
// Makes sure te program still runs and doesn't panic
let addr = match "::1".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 65535, 100, Duration::from_millis(100), false);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn quad_zero_scanner_runs() {
let addr = match "0.0.0.0".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 1000, 100, Duration::from_millis(500), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn zero_ports_no_return_no_panic() {
let addr = match "0.0.0.0".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 1, 1, 100, Duration::from_millis(50), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn backwards_ports_scanner_runs() {
let addr = match "0.0.0.0".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 10, 1, 100, Duration::from_millis(50), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn google_dns_runs() {
let addr = match "8.8.8.8".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
let scanner = Scanner::new(addr, 400, 445, 100, Duration::from_millis(1500), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn infer_ulimit_lowering_no_panic() {
// this test is because of a bug where Mac OS didn't automatically lower ulimit
let addr = match "8.8.8.8".parse::<IpAddr>() {
Ok(res) => res,
Err(_) => panic!("Could not parse IP Address"),
};
// mac should have this automatically scaled down
let scanner = Scanner::new(addr, 400, 600, 10_000, Duration::from_millis(1500), true);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn batch_size_lowered() {
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
ips: vec![IpAddr::from_str("127.0.0.1").unwrap()],
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
};
let batch_size = infer_batch_size(&opts, 120);
@@ -349,65 +312,74 @@ mod tests {
#[test]
fn batch_size_lowered_average_size() {
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
ips: vec![IpAddr::from_str("127.0.0.1").unwrap()],
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
};
let batch_size = infer_batch_size(&opts, 9000);
let batch_size = infer_batch_size(&opts, 9_000);
assert!(batch_size == 3000);
assert!(batch_size == 3_000);
}
#[test]
fn batch_size_equals_ulimit_lowered() {
// because ulimit and batch size are same size, batch size is lowered
// to ULIMIT - 100
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
ips: vec![IpAddr::from_str("127.0.0.1").unwrap()],
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
};
let batch_size = infer_batch_size(&opts, 5000);
let batch_size = infer_batch_size(&opts, 5_000);
assert!(batch_size == 4900);
assert!(batch_size == 4_900);
}
#[test]
fn batch_size_adjusted_2000() {
// ulimit == batch_size
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
ips: vec![IpAddr::from_str("127.0.0.1").unwrap()],
quiet: true,
batch_size: 50_000,
timeout: 1000,
ulimit: Some(2000),
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
};
let batch_size = adjust_ulimit_size(&opts);
assert!(batch_size == 2000);
assert!(batch_size == 2_000);
}
#[test]
fn test_print_opening_no_panic() {
// print opening should not paniic
// print opening should not panic
print_opening();
assert!(1 == 1);
}
#[test]
fn test_high_ulimit_no_quiet_mode() {
let opts = Opts {
ip: IpAddr::from_str("127.0.0.1").unwrap(),
ips: vec![IpAddr::from_str("127.0.0.1").unwrap()],
quiet: false,
batch_size: 10,
timeout: 1000,
timeout: 1_000,
ulimit: None,
command: Vec::new(),
accessible: true,
scan_order: ScanOrder::Serial,
};
let batch_size = infer_batch_size(&opts, 1000000);
infer_batch_size(&opts, 1_000_000);
assert!(1 == 1);
}
+74
View File
@@ -0,0 +1,74 @@
mod range_iterator;
use super::ScanOrder;
use range_iterator::RangeIterator;
pub enum PortStrategy {
Serial(SerialRange),
Random(RandomRange),
}
impl PortStrategy {
pub fn pick(start: u16, end: u16, scan_type: ScanOrder) -> Self {
match scan_type {
ScanOrder::Serial => PortStrategy::Serial(SerialRange { start, end }),
ScanOrder::Random => PortStrategy::Random(RandomRange { start, end }),
}
}
pub fn order(&self) -> Vec<u16> {
match self {
PortStrategy::Serial(range) => range.generate(),
PortStrategy::Random(range) => range.generate(),
}
}
}
trait RangeOrder {
fn generate(&self) -> Vec<u16>;
}
pub struct SerialRange {
start: u16,
end: u16,
}
impl RangeOrder for SerialRange {
fn generate(&self) -> Vec<u16> {
(self.start..self.end).collect()
}
}
pub struct RandomRange {
start: u16,
end: u16,
}
impl RangeOrder for RandomRange {
fn generate(&self) -> Vec<u16> {
RangeIterator::new(self.start.into(), self.end.into()).collect()
}
}
#[cfg(test)]
mod tests {
use super::PortStrategy;
use crate::ScanOrder;
#[test]
fn serial_strategy() {
let strategy = PortStrategy::pick(1, 100, ScanOrder::Serial);
let result = strategy.order();
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
}
#[test]
fn random_strategy() {
let strategy = PortStrategy::pick(1, 100, ScanOrder::Random);
let mut result = strategy.order();
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_ne!(expected_range, result);
result.sort();
assert_eq!(expected_range, result);
}
}
+129
View File
@@ -0,0 +1,129 @@
use gcd::Gcd;
use rand::Rng;
pub struct RangeIterator {
active: bool,
normalized_end: u32,
normalized_first_pick: u32,
normalized_pick: u32,
actual_start: u32,
step: u32,
}
/// An iterator that follows the `Linear Congruential Generator` algorithm.
///
/// For more information: https://en.wikipedia.org/wiki/Linear_congruential_generator
impl RangeIterator {
/// Receives the the start and end of a range and normalize
/// these values before selecting a coprime for the end of the range
/// which will server as the step for the algorithm.
///
/// For example, the range `1000-2500` will be normalized to `0-1500`
/// before going through the algorithm.
pub fn new(start: u32, end: u32) -> Self {
let normalized_end = end - start;
let step = pick_random_coprime(normalized_end);
// Randomly choose a number within the range to be the first
// and assign it as a pick.
let mut rng = rand::thread_rng();
let normalized_first_pick = rng.gen_range(0, normalized_end);
Self {
active: true,
normalized_end,
step,
normalized_first_pick,
normalized_pick: normalized_first_pick,
actual_start: start,
}
}
}
impl Iterator for RangeIterator {
type Item = u16;
// The next step is always bound by the formula: N+1 = (N + STEP) % TOP_OF_THE_RANGE
// It will only stop once we generate a number equal to the first generated number.
fn next(&mut self) -> Option<Self::Item> {
if !self.active {
return None;
}
let current_pick = self.normalized_pick;
let next_pick = (current_pick + self.step) % self.normalized_end;
// If the next pick is equal to the first pick this means that
// we have iterated through the entire range.
if next_pick == self.normalized_first_pick {
self.active = false;
}
self.normalized_pick = next_pick;
Some((self.actual_start + current_pick) as u16)
}
}
/// The probability that two random integers are coprime to one another
/// works out to be around 61%, given that we can safely pick a random
/// number and test it. Just in case we are having a bad day and we cannot
/// pick a coprime number after 10 tries we just return "end - 1" which
/// is guaranteed to be a coprime, but won't provide ideal randomization.
///
/// We pick between "lower_range" and "upper_range" since values too close to
/// the boundaries, which in these case are the "start" and "end" arguments
/// would also provide non-ideal randomization as discussed on the paragraph
/// above.
fn pick_random_coprime(end: u32) -> u32 {
let range_boundary = end / 4;
let lower_range = range_boundary;
let upper_range = end - range_boundary;
let mut rng = rand::thread_rng();
let mut candidate = rng.gen_range(lower_range, upper_range);
for _ in 0..10 {
if end.gcd(candidate) == 1 {
return candidate;
} else {
candidate = rng.gen_range(lower_range, upper_range);
}
}
end - 1
}
#[cfg(test)]
mod tests {
use super::RangeIterator;
#[test]
fn range_iterator_iterates_through_the_entire_range() {
let result = generate_sorted_range(1, 10);
let expected_range = (1..10).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1, 100);
let expected_range = (1..100).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1, 1000);
let expected_range = (1..1000).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1, 65_535);
let expected_range = (1..65_535).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
let result = generate_sorted_range(1000, 2000);
let expected_range = (1000..2000).into_iter().collect::<Vec<u16>>();
assert_eq!(expected_range, result);
}
fn generate_sorted_range(start: u32, end: u32) -> Vec<u16> {
let range = RangeIterator::new(start, end);
let mut result = range.into_iter().collect::<Vec<u16>>();
result.sort();
result
}
}
-139
View File
@@ -1,139 +0,0 @@
use async_std::io;
use async_std::net::TcpStream;
use async_std::prelude::*;
use colored::*;
use futures::stream::FuturesUnordered;
use std::time::Duration;
use std::{
io::ErrorKind,
net::{IpAddr, Shutdown, SocketAddr},
};
/// The class for the scanner
/// Host is data type IpAddr and is the host address
/// start & end is where the port scan starts and ends
/// batch_size is how many ports at a time should be scanned
/// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration.
/// Quiet is whether or not RustScan should print things, or wait until the end to print only open ports.
#[cfg(not(tarpaulin_include))]
pub struct Scanner {
host: IpAddr,
start: u16,
end: u16,
batch_size: u32,
timeout: Duration,
quiet: bool,
}
impl Scanner {
pub fn new(
host: IpAddr,
start: u16,
end: u16,
batch_size: u32,
timeout: Duration,
quiet: bool,
) -> Self {
Self {
host: host.to_owned(),
start,
end,
batch_size,
timeout,
quiet,
}
}
/// Runs scan_range with chunk sizes
/// If you want to run RustScan normally, this is the entry point used
/// Returns all open ports as Vec<u16>
pub async fn run(&self) -> Vec<u16> {
let ports: Vec<u16> = (self.start..self.end).collect();
let mut open_ports: std::vec::Vec<u16> = Vec::new();
// TODO change this to port size
// to fix bug when we introduce custom port ranges
for range in ports.chunks(self.batch_size as usize) {
let mut ports = self.scan_range(range).await;
open_ports.append(&mut ports);
}
open_ports
}
/// Given a range of ports, scan them all.
/// Returns a vector of open ports.
async fn scan_range(&self, range: &[u16]) -> Vec<u16> {
let mut ftrs = FuturesUnordered::new();
for port in range {
ftrs.push(self.scan_port(*port));
}
let mut open_ports: Vec<u16> = Vec::new();
while let Some(result) = ftrs.next().await {
match result {
Ok(port) => open_ports.push(port),
_ => {}
}
}
open_ports
}
/// Given a port, scan it.
/// Turns the address into a SocketAddr
/// Deals with the <result> type
/// If it experiences error ErrorKind::Other then too many files are open and it Panics!
/// ese any other error, it returns the error in Result as a string
/// If no errors occur, it returns the port number in Result to signify the port is open.
/// This function mainly deals with the logic of Results handling.
/// # Example
///
/// self.scan_port(10:u16)
///
/// Note: `self` must contain `self.host`.
async fn scan_port(&self, port: u16) -> io::Result<u16> {
let addr = SocketAddr::new(self.host, port);
// println!("{:?}", addr);
match self.connect(addr).await {
Ok(x) => {
// match stream_result.shutdown(Shutdown::Both)
info!("Shutting down stream");
match x.shutdown(Shutdown::Both) {
_ => {}
}
if !self.quiet {
println!("Open {}", port.to_string().purple());
}
// if connection successful
// shut down stream
// return port
Ok(port)
}
Err(e) => match e.kind() {
ErrorKind::Other => {
panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
}
_ => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
},
}
}
/// Performs the connection to the socket with timeout
/// # Example
///
/// let port: u16 = 80
/// // Host is an IpAddr type
/// let host = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
/// let addr = SocketAddr::new(host, port)
/// self.connect(addr)
/// // returns Result which is either Ok(stream) for port is open, or Er for port is closed.
/// // Timeout occurs after self.timeout seconds
///
async fn connect(&self, addr: SocketAddr) -> io::Result<TcpStream> {
let stream =
io::timeout(self.timeout, async move { TcpStream::connect(addr).await }).await?;
info!("Returning okay from connect");
Ok(stream)
}
}
+193
View File
@@ -0,0 +1,193 @@
use super::PortStrategy;
use async_std::io;
use async_std::net::TcpStream;
use async_std::prelude::*;
use colored::*;
use futures::stream::FuturesUnordered;
use std::time::Duration;
use std::{
io::ErrorKind,
net::{IpAddr, Shutdown, SocketAddr},
};
/// The class for the scanner
/// Host is data type IpAddr and is the host address
/// start & end is where the port scan starts and ends
/// batch_size is how many ports at a time should be scanned
/// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration.
/// Quiet is whether or not RustScan should print things, or wait until the end to print only open ports.
#[cfg(not(tarpaulin_include))]
pub struct Scanner {
hosts: Vec<IpAddr>,
batch_size: u16,
timeout: Duration,
quiet: bool,
port_strategy: PortStrategy,
}
impl Scanner {
pub fn new(
hosts: &[IpAddr],
batch_size: u16,
timeout: Duration,
quiet: bool,
port_strategy: PortStrategy,
) -> Self {
Self {
batch_size,
timeout,
quiet,
port_strategy,
hosts: hosts.iter().map(|host| host.to_owned()).collect(),
}
}
/// Runs scan_range with chunk sizes
/// If you want to run RustScan normally, this is the entry point used
/// Returns all open ports as Vec<u16>
pub async fn run(&self) -> Vec<SocketAddr> {
let ports: Vec<u16> = self.port_strategy.order();
let batch_per_host: usize = self.batch_size as usize / self.hosts.len();
let mut open_sockets: Vec<SocketAddr> = Vec::new();
for batch in ports.chunks(batch_per_host) {
let mut sockets = self.scan_ports(batch).await;
open_sockets.append(&mut sockets);
}
open_sockets
}
/// Given a slice of sockets, scan them all.
/// Returns a vector of open sockets.
async fn scan_ports(&self, ports: &[u16]) -> Vec<SocketAddr> {
let mut ftrs = FuturesUnordered::new();
for port in ports {
for host in &self.hosts {
ftrs.push(self.scan_socket(SocketAddr::new(*host, *port)));
}
}
let mut open_sockets: Vec<SocketAddr> = Vec::new();
while let Some(result) = ftrs.next().await {
match result {
Ok(socket) => open_sockets.push(socket),
_ => {}
}
}
open_sockets
}
/// Given a port, scan it.
/// Turns the address into a SocketAddr
/// Deals with the <result> type
/// If it experiences error ErrorKind::Other then too many files are open and it Panics!
/// ese any other error, it returns the error in Result as a string
/// If no errors occur, it returns the port number in Result to signify the port is open.
/// This function mainly deals with the logic of Results handling.
/// # Example
///
/// self.scan_port(10:u16)
///
/// Note: `self` must contain `self.host`.
async fn scan_socket(&self, socket: SocketAddr) -> io::Result<SocketAddr> {
match self.connect(socket).await {
Ok(x) => {
// match stream_result.shutdown(Shutdown::Both)
info!("Shutting down stream");
match x.shutdown(Shutdown::Both) {
_ => {}
}
if !self.quiet {
println!("Open {}", socket.to_string().purple());
}
Ok(socket)
}
Err(e) => match e.kind() {
ErrorKind::Other => {
panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
}
_ => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
},
}
}
/// Performs the connection to the socket with timeout
/// # Example
///
/// let port: u16 = 80
/// // Host is an IpAddr type
/// let host = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
/// let socket = SocketAddr::new(host, port);
/// self.connect(socket)
/// // returns Result which is either Ok(stream) for port is open, or Er for port is closed.
/// // Timeout occurs after self.timeout seconds
///
async fn connect(&self, socket: SocketAddr) -> io::Result<TcpStream> {
let stream = io::timeout(
self.timeout,
async move { TcpStream::connect(socket).await },
)
.await?;
Ok(stream)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ScanOrder;
use async_std::task::block_on;
use std::{net::IpAddr, time::Duration};
#[test]
fn scanner_runs() {
// Makes sure the program still runs and doesn't panic
let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn ipv6_scanner_runs() {
// Makes sure the program still runs and doesn't panic
let addrs = vec!["::1".parse::<IpAddr>().unwrap()];
let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
block_on(scanner.run());
// if the scan fails, it wouldn't be able to assert_eq! as it panicked!
assert_eq!(1, 1);
}
#[test]
fn quad_zero_scanner_runs() {
let addrs = vec!["0.0.0.0".parse::<IpAddr>().unwrap()];
let strategy = PortStrategy::pick(1, 1_000, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn google_dns_runs() {
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
let strategy = PortStrategy::pick(400, 445, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
block_on(scanner.run());
assert_eq!(1, 1);
}
#[test]
fn infer_ulimit_lowering_no_panic() {
// Test behaviour on MacOS where ulimit is not automatically lowered
let addrs = vec!["8.8.8.8".parse::<IpAddr>().unwrap()];
// mac should have this automatically scaled down
let strategy = PortStrategy::pick(400, 600, ScanOrder::Random);
let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy);
block_on(scanner.run());
assert_eq!(1, 1);
}
}
+64
View File
@@ -0,0 +1,64 @@
/// Terminal User Interface Module for RustScan
/// Defines macros to use
#[macro_export]
macro_rules! warning {
($name:expr) => {
use ansi_term::Colour::Red;
println!("{} {}", Red.bold().paint("[!]"), $name);
};
// quiet mode
($name:expr, $quiet:expr) => {
use ansi_term::Colour::Red;
if !$quiet {
println!("{} {}", Red.bold().paint("[!]"), $name);
}
};
}
#[macro_export]
macro_rules! detail {
($name:expr) => {
use ansi_term::Colour::Blue;
println!("{} {}", Blue.bold().paint("[~]"), $name);
};
($name:expr, $quiet:expr) => {
use ansi_term::Colour::Blue;
if !$quiet {
println!("{} {}", Blue.bold().paint("[~]"), $name);
}
};
}
#[macro_export]
macro_rules! output {
($name:expr, $quiet:expr) => {
use ansi_term::Colour::RGB;
if !$quiet {
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
}
};
($name:expr) => {
use ansi_term::Colour::RGB;
println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
};
}
#[macro_export]
macro_rules! funny_opening {
// prints a funny quote / opening
() => {
use rand::seq::SliceRandom;
let quotes = vec![
"Nmap? More like slowmap.🐢",
"🌍HACK THE PLANET🌍",
"Real hackers hack time ⌛",
"Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan",
"😵 https://admin.tryhackme.com",
];
let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap();
println!("{}\n", random_quote);
// println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
};
}