Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ jobs:
command: fmt
args: --all -- --check

clippy:
name: Clippy linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features

links:
name: markdown-link-check
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion src/benchmark/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub struct NamedTimer {
impl NamedTimer {
pub fn start(name: &'static str) -> Self {
Self {
name: name,
name,
start: Some(Instant::now()),
end: None,
}
Expand Down
14 changes: 6 additions & 8 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct PortRange {
#[cfg(not(tarpaulin_include))]
fn parse_range(input: &str) -> Result<PortRange, String> {
let range = input
.split("-")
.split('-')
.map(|x| x.parse::<u16>())
.collect::<Result<Vec<u16>, std::num::ParseIntError>>();

Expand Down Expand Up @@ -189,14 +189,12 @@ impl Opts {
}

// Only use top ports when the user asks for them
if self.top {
if config.ports.is_some() {
let mut ports: Vec<u16> = Vec::with_capacity(config.ports.clone().unwrap().len());
for entry in config.ports.clone().unwrap().keys() {
ports.push(entry.parse().unwrap())
}
self.ports = Some(ports);
if self.top && config.ports.is_some() {
let mut ports: Vec<u16> = Vec::with_capacity(config.ports.clone().unwrap().len());
for entry in config.ports.clone().unwrap().keys() {
ports.push(entry.parse().unwrap())
}
self.ports = Some(ports);
}

merge_optional!(range, ulimit);
Expand Down
118 changes: 46 additions & 72 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,13 @@ fn main() {

let mut script_bench = NamedTimer::start("Scripts");
for (ip, ports) in ports_per_ip.iter_mut() {
let vec_str_ports: Vec<String> = ports.into_iter().map(|port| port.to_string()).collect();
let vec_str_ports: Vec<String> = ports.iter().map(|port| port.to_string()).collect();

// nmap port style is 80,443. Comma separated with no spaces.
let ports_str = vec_str_ports.join(",");

// if option scripts is none, no script will be spawned
if opts.greppable || opts.scripts.clone() == ScriptsRequired::None {
if opts.greppable || opts.scripts == ScriptsRequired::None {
println!("{} -> [{}]", &ip, ports_str);
continue;
}
Expand All @@ -136,12 +136,12 @@ fn main() {
for mut script_f in scripts_to_run.clone() {
output!(
format!("Script to be run {:?}\n", script_f.call_format,),
opts.greppable.clone(),
opts.accessible.clone()
opts.greppable,
opts.accessible
);

// This part allows us to add commandline arguments to the Script call_format, appending them to the end of the command.
if opts.command.len() > 0 {
if !opts.command.is_empty() {
let user_extra_args: Vec<String> = shell_words::split(&opts.command.join(" "))
.expect("Failed to parse extra user commandline arguments");
if script_f.call_format.is_some() {
Expand All @@ -163,11 +163,7 @@ fn main() {
);
match script.run() {
Ok(script_result) => {
detail!(
format!("{}", script_result),
opts.greppable,
opts.accessible
);
detail!(script_result.to_string(), opts.greppable, opts.accessible);
}
Err(e) => {
warning!(
Expand Down Expand Up @@ -205,14 +201,12 @@ The Modern Day Port Scanner."#;
println!("{}", info.gradient(Color::Yellow).bold());
funny_opening!();

let mut home_dir = match dirs::home_dir() {
Some(dir) => dir,
None => panic!("Could not infer config file path."),
};
home_dir.push(".rustscan.toml");
let config_path = dirs::home_dir()
.expect("Could not infer config file path.")
.join(".rustscan.toml");

detail!(
format!("The config file is expected to be at {:?}", home_dir),
format!("The config file is expected to be at {:?}", config_path),
opts.greppable,
opts.accessible
);
Expand All @@ -227,21 +221,11 @@ fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
&Resolver::new(ResolverConfig::cloudflare_tls(), ResolverOpts::default()).unwrap();

for address in &input.addresses {
match parse_address(address, resolver) {
Ok(parsed_ips) => {
if !parsed_ips.is_empty() {
ips.extend(parsed_ips);
} else {
unresolved_addresses.push(address);
}
}
_ => {
warning!(
format!("Host {:?} could not be resolved.", address),
input.greppable,
input.accessible
);
}
let parsed_ips = parse_address(address, resolver);
if !parsed_ips.is_empty() {
ips.extend(parsed_ips);
} else {
unresolved_addresses.push(address);
}
}

Expand All @@ -259,15 +243,14 @@ fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
continue;
}

match read_ips_from_file(file_path, &resolver) {
Ok(x) => ips.extend(x),
_ => {
warning!(
format!("Host {:?} could not be resolved.", file_path),
input.greppable,
input.accessible
);
}
if let Ok(x) = read_ips_from_file(file_path, &resolver) {
ips.extend(x);
} else {
warning!(
format!("Host {:?} could not be resolved.", file_path),
input.greppable,
input.accessible
);
}
}

Expand All @@ -277,29 +260,25 @@ fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
/// Given a string, parse it as an host, IP address, or CIDR.
/// This allows us to pass files as hosts or cidr or IPs easily
/// Call this everytime you have a possible IP_or_host
fn parse_address(address: &str, resolver: &Resolver) -> Result<Vec<IpAddr>, std::io::Error> {
let mut ips: Vec<IpAddr> = Vec::new();

match IpCidr::from_str(&address) {
Ok(cidr) => cidr.iter().for_each(|ip| ips.push(ip)),
_ => match format!("{}:{}", &address, 80).to_socket_addrs() {
Ok(mut iter) => ips.push(iter.nth(0).unwrap().ip()),
_ => match resolve_ips_from_host(address, resolver) {
Ok(hosts) => ips.extend(hosts),
_ => (),
},
},
};

Ok(ips)
fn parse_address(address: &str, resolver: &Resolver) -> Vec<IpAddr> {
IpCidr::from_str(&address)
.map(|cidr| cidr.iter().collect())
.ok()
.or_else(|| {
format!("{}:{}", &address, 80)
.to_socket_addrs()
.ok()
.map(|mut iter| vec![iter.next().unwrap().ip()])
})
.unwrap_or_else(|| resolve_ips_from_host(address, resolver))
}

/// Uses DNS to get the IPS assiocated with host
fn resolve_ips_from_host(source: &str, resolver: &Resolver) -> Result<Vec<IpAddr>, std::io::Error> {
match resolver.lookup_ip(&source) {
Ok(x) => Ok(x.iter().collect()),
_ => Ok(Vec::new()),
}
fn resolve_ips_from_host(source: &str, resolver: &Resolver) -> Vec<IpAddr> {
resolver
.lookup_ip(source)
.map(|x| x.iter().collect())
.unwrap_or_default()
}

#[cfg(not(tarpaulin_include))]
Expand All @@ -314,18 +293,13 @@ fn read_ips_from_file(
let mut ips: Vec<std::net::IpAddr> = Vec::new();

for address_line in reader.lines() {
match address_line {
Ok(address) => match parse_address(&address, resolver) {
Ok(result) => ips.extend(result),
Err(e) => {
debug!("{} is not a valid IP or host", e);
}
},
Err(_) => {
debug!("Line in file is not valid");
}
if let Ok(address) = address_line {
ips.extend(parse_address(&address, resolver));
} else {
debug!("Line in file is not valid");
}
}

Ok(ips)
}

Expand Down Expand Up @@ -385,8 +359,8 @@ fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 {
// When the ulimit is higher than the batch size let the user know that the
// batch size can be increased unless they specified the ulimit themselves.
else if ulimit + 2 > batch_size && (opts.ulimit.is_none()) {
detail!(format!("File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", ulimit - 100),
opts.greppable, opts.accessible);
detail!(format!("File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", ulimit - 100),
opts.greppable, opts.accessible);
}

batch_size as u16
Expand Down
7 changes: 3 additions & 4 deletions src/scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,15 @@ impl Scanner {
self.batch_size,
self.ips.len(),
&ports.len(),
(self.ips.len() * &ports.len()));
(self.ips.len() * ports.len()));

while let Some(result) = ftrs.next().await {
if let Some(socket) = socket_iterator.next() {
ftrs.push(self.scan_socket(socket));
}

match result {
Ok(socket) => open_sockets.push(socket),
_ => {}
if let Ok(socket) = result {
open_sockets.push(socket);
}
}
debug!("Open Sockets found: {:?}", &open_sockets);
Expand Down
4 changes: 2 additions & 2 deletions src/scanner/socket_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ pub struct SocketIterator<'s> {
/// generating a vector containing all these combinations.
impl<'s> SocketIterator<'s> {
pub fn new(ips: &'s [IpAddr], ports: &'s [u16]) -> Self {
let ports_it = Box::new(ports.into_iter());
let ips_it = Box::new(ips.into_iter());
let ports_it = Box::new(ports.iter());
let ips_it = Box::new(ips.iter());
Self {
product_it: iproduct!(ports_it, ips_it),
}
Expand Down
37 changes: 17 additions & 20 deletions src/scripts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use std::path::PathBuf;
use subprocess::{Exec, ExitStatus};
use text_placeholder::Template;

static DEFAULT: &'static str = r#"tags = ["core_approved", "RustScan", "default"]
static DEFAULT: &str = r#"tags = ["core_approved", "RustScan", "default"]
developer = [ "RustScan", "https://github.com/RustScan" ]
ports_separator = ","
call_format = "nmap -vvv -p {{port}} {{ip}}"
Expand Down Expand Up @@ -176,13 +176,13 @@ impl Script {
call_format: Option<String>,
) -> Self {
Self {
path: path,
ip: ip,
open_ports: open_ports,
trigger_port: trigger_port,
ports_separator: ports_separator,
tags: tags,
call_format: call_format,
path,
ip,
open_ports,
trigger_port,
ports_separator,
tags,
call_format,
}
}

Expand All @@ -191,7 +191,7 @@ impl Script {
pub fn run(self) -> Result<String> {
debug!("run self {:?}", &self);

let separator = self.ports_separator.unwrap_or(",".into());
let separator = self.ports_separator.unwrap_or_else(|| ",".into());

let mut ports_str = self
.open_ports
Expand Down Expand Up @@ -231,17 +231,14 @@ impl Script {

let arguments = shell_words::split(
&to_run
.split(" ")
.split(' ')
.map(|arg| arg.to_string())
.collect::<Vec<String>>()
.join(" "),
)
.expect("Failed to parse script arguments");

match execute_script(arguments) {
Ok(result) => return Ok(result),
Err(e) => return Err(e),
}
execute_script(arguments)
}
}

Expand All @@ -264,7 +261,7 @@ fn execute_script(mut arguments: Vec<String>) -> Result<String> {
}
Err(error) => {
debug!("Command error {}", error.to_string());
return Err(anyhow!(error.to_string()));
Err(anyhow!(error.to_string()))
}
}
}
Expand All @@ -278,9 +275,9 @@ pub fn find_scripts(mut path: PathBuf) -> Result<Vec<PathBuf>> {
let entry = entry?;
files_vec.push(entry.path());
}
return Ok(files_vec);
Ok(files_vec)
} else {
return Err(anyhow!("Can't find scripts folder"));
Err(anyhow!("Can't find scripts folder"))
}
}

Expand All @@ -301,7 +298,7 @@ impl ScriptFile {
if let Ok(file) = File::open(script) {
for line in io::BufReader::new(file).lines().skip(1) {
if let Ok(mut line) = line {
if line.starts_with("#") {
if line.starts_with('#') {
line.retain(|c| c != '#');
line = line.trim().to_string();
line.push_str("\n");
Expand All @@ -322,11 +319,11 @@ impl ScriptFile {
debug!("Parsed ScriptFile{} \n{:?}", &real_path.display(), &parsed);
parsed.path = Some(real_path);
// parsed_scripts.push(parsed);
return Some(parsed);
Some(parsed)
}
Err(e) => {
debug!("Failed to parse ScriptFile headers {}", e.to_string());
return None;
None
}
}
}
Expand Down