Skip to content
Closed
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
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ itertools = "0.13.0"
hickory-resolver = { version = "0.24.0", features = ["dns-over-rustls"] }
anyhow = "1.0.40"
text_placeholder = { version = "0.5", features = ["struct_context"] }
once_cell = "1.20.2"

[dev-dependencies]
parameterized = "2.0.0"
Expand All @@ -59,6 +58,10 @@ strip = true
name = "rustscan"
path = "src/main.rs"

[[example]]
name = "log_mode_test_binary"
path = "tests/log_mode_test_binary/mod.rs"

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(tarpaulin_include)"] }

Expand Down
5 changes: 2 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ fn generate_code(port_payload_map: BTreeMap<Vec<u16>, Vec<u8>>) {
let dest_path = PathBuf::from("src/generated.rs");

let mut generated_code = String::new();
generated_code.push_str("use std::collections::BTreeMap;\n");
generated_code.push_str("use once_cell::sync::Lazy;\n\n");
generated_code.push_str("use std::{collections::BTreeMap, sync::LazyLock};\n\n");

generated_code.push_str("fn generated_data() -> BTreeMap<Vec<u16>, Vec<u8>> {\n");
generated_code.push_str(" let mut map = BTreeMap::new();\n");
Expand Down Expand Up @@ -92,7 +91,7 @@ fn generate_code(port_payload_map: BTreeMap<Vec<u16>, Vec<u8>>) {
generated_code.push_str("}\n\n");

generated_code.push_str(
"static PARSED_DATA: Lazy<BTreeMap<Vec<u16>, Vec<u8>>> = Lazy::new(generated_data);\n",
"static PARSED_DATA: LazyLock<BTreeMap<Vec<u16>, Vec<u8>>> = LazyLock::new(generated_data);\n",
);
generated_code.push_str("pub fn get_parsed_data() -> &'static BTreeMap<Vec<u16>, Vec<u8>> {\n");
generated_code.push_str(" &PARSED_DATA\n");
Expand Down
5 changes: 2 additions & 3 deletions src/generated.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use once_cell::sync::Lazy;
use std::collections::BTreeMap;
use std::{collections::BTreeMap, sync::LazyLock};

fn generated_data() -> BTreeMap<Vec<u16>, Vec<u8>> {
let mut map = BTreeMap::new();
Expand Down Expand Up @@ -2990,7 +2989,7 @@ fn generated_data() -> BTreeMap<Vec<u16>, Vec<u8>> {
map
}

static PARSED_DATA: Lazy<BTreeMap<Vec<u16>, Vec<u8>>> = Lazy::new(generated_data);
static PARSED_DATA: LazyLock<BTreeMap<Vec<u16>, Vec<u8>>> = LazyLock::new(generated_data);
pub fn get_parsed_data() -> &'static BTreeMap<Vec<u16>, Vec<u8>> {
&PARSED_DATA
}
4 changes: 3 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! Provides a means to read, parse and hold configuration options for scans.
use crate::print_log;

use clap::{Parser, ValueEnum};
use serde_derive::Deserialize;
use std::collections::HashMap;
Expand Down Expand Up @@ -298,7 +300,7 @@ impl Config {
let config: Config = match toml::from_str(&content) {
Ok(config) => config,
Err(e) => {
println!("Found {e} in configuration file.\nAborting scan.\n");
print_log!(error, "Found {e} in configuration file.\nAborting scan.\n");
std::process::exit(1);
}
};
Expand Down
13 changes: 13 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
//! ```
#![allow(clippy::needless_doctest_main)]

use std::sync::OnceLock;

pub mod tui;

pub mod input;
Expand All @@ -56,3 +58,14 @@ pub mod scripts;
pub mod address;

pub mod generated;

/// Static variable defining the current state of execution. The cli binary should
/// set it to true by calling set_cli_mode.
#[doc(hidden)]
pub static IS_CLI_MODE: OnceLock<bool> = OnceLock::new();

/// Set IS_CLI_MODE to true.
#[doc(hidden)]
pub fn set_cli_mode() {
let _ = IS_CLI_MODE.set(true);
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustscan::input::{self, Config, Opts, ScriptsRequired};
use rustscan::port_strategy::PortStrategy;
use rustscan::scanner::Scanner;
use rustscan::scripts::{init_scripts, Script, ScriptFile};
use rustscan::set_cli_mode;
use rustscan::{detail, funny_opening, output, warning};

use colorful::{Color, Colorful};
Expand Down Expand Up @@ -38,6 +39,7 @@ fn main() {
#[cfg(not(unix))]
let _ = ansi_term::enable_ansi_support();

set_cli_mode();
env_logger::init();
let mut benchmarks = Benchmark::init();
let mut rustscan_bench = NamedTimer::start("RustScan");
Expand Down
7 changes: 4 additions & 3 deletions src/scanner/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Core functionality for actual scanning behaviour.
use crate::generated::get_parsed_data;
use crate::port_strategy::PortStrategy;
use crate::print_log;
use log::debug;

mod socket_iterator;
Expand Down Expand Up @@ -290,7 +291,7 @@ impl Scanner {
}
}
Err(e) => {
println!("Err E binding sock {:?}", e);
print_log!(error, "Err E binding sock {:?}", e);
Err(e)
}
}
Expand All @@ -300,9 +301,9 @@ impl Scanner {
fn fmt_ports(&self, socket: SocketAddr) {
if !self.greppable {
if self.accessible {
println!("Open {socket}");
print_log!(info, "Open {socket}");
} else {
println!("Open {}", socket.to_string().purple());
print_log!(info, "Open {}", socket.to_string().purple());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/scanner/socket_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl<'s> SocketIterator<'s> {
}

#[allow(clippy::doc_link_with_quotes)]
impl<'s> Iterator for SocketIterator<'s> {
impl Iterator for SocketIterator<'_> {
type Item = SocketAddr;

/// Returns a socket based on the combination of one of the provided
Expand Down
60 changes: 51 additions & 9 deletions src/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@
#[macro_export]
macro_rules! warning {
($name:expr) => {
println!("{} {}", ansi_term::Colour::Red.bold().paint("[!]"), $name);
$crate::print_log!(
warn,
"{} {}",
ansi_term::Colour::Red.bold().paint("[!]"),
$name
);
};
($name:expr, $greppable:expr, $accessible:expr) => {
// if not greppable then print, otherwise no else statement so do not print.
if !$greppable {
if $accessible {
// Don't print the ascii art
println!("{}", $name);
$crate::print_log!(warn, "{}", $name);
} else {
println!("{} {}", ansi_term::Colour::Red.bold().paint("[!]"), $name);
$crate::print_log!(
warn,
"{} {}",
ansi_term::Colour::Red.bold().paint("[!]"),
$name
);
}
}
};
Expand All @@ -23,16 +33,26 @@ macro_rules! warning {
#[macro_export]
macro_rules! detail {
($name:expr) => {
println!("{} {}", ansi_term::Colour::Blue.bold().paint("[~]"), $name);
$crate::print_log!(
info,
"{} {}",
ansi_term::Colour::Blue.bold().paint("[~]"),
$name
);
};
($name:expr, $greppable:expr, $accessible:expr) => {
// if not greppable then print, otherwise no else statement so do not print.
if !$greppable {
if $accessible {
// Don't print the ascii art
println!("{}", $name);
$crate::print_log!(info, "{}", $name);
} else {
println!("{} {}", ansi_term::Colour::Blue.bold().paint("[~]"), $name);
$crate::print_log!(
info,
"{} {}",
ansi_term::Colour::Blue.bold().paint("[~]"),
$name
);
}
}
};
Expand All @@ -41,7 +61,8 @@ macro_rules! detail {
#[macro_export]
macro_rules! output {
($name:expr) => {
println!(
$crate::print_log!(
info,
"{} {}",
RGansi_term::Colour::RGB(0, 255, 9).bold().paint("[>]"),
$name
Expand All @@ -52,9 +73,10 @@ macro_rules! output {
if !$greppable {
if $accessible {
// Don't print the ascii art
println!("{}", $name);
$crate::print_log!(info, "{}", $name);
} else {
println!(
$crate::print_log!(
info,
"{} {}",
ansi_term::Colour::RGB(0, 255, 9).bold().paint("[>]"),
$name
Expand Down Expand Up @@ -103,3 +125,23 @@ macro_rules! funny_opening {
println!("{}\n", random_quote);
};
}

/// Wrapper macro for printing/logging wraps println! and log::$level!
/// 1. if rustscan::IS_CLI_MODE is true calls `println!`
/// 2. if rustscan::IS_CLI_MODE is undefined or false `log::$level!` also sets IS_CLI_MODE
/// to false if it was previously undefined.
///
/// Library code should call this macro to print information that the binary
/// is expected to print to stdout and library is expected to log at a
/// level specified by parameter $level.
#[doc(hidden)]
#[macro_export]
macro_rules! print_log {
($level:ident, $($fmt_args:tt)*) => {
if *$crate::IS_CLI_MODE.get_or_init(|| false) {
println!($($fmt_args)*);
} else {
log::$level!($($fmt_args)*);
}
}
}
28 changes: 28 additions & 0 deletions tests/log_mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Test rustscan logging utilities, ensuring library doesn't log to stdout.
*/
use std::{
io::Read,
process::{Command, Stdio},
};

// need to import here, otherwise cargo thinks I'm not using the test
mod log_mode_test_binary;

#[test]
fn no_logging_scanner() {
let mut child = Command::new("target/debug/examples/log_mode_test_binary")
.stderr(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();

child.wait().unwrap();

let buf = &mut Vec::new();
child.stdout.take().unwrap().read_to_end(buf).unwrap();
assert!(buf.is_empty());

child.stderr.take().unwrap().read_to_end(buf).unwrap();
assert!(buf.is_empty());
}
39 changes: 39 additions & 0 deletions tests/log_mode_test_binary/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//! File used just to build a binary for testing if stdout is being logged.
//! Older versions of the library rustscan would write to stdout. This file
//! helps ensure the library only writes to stdout if log is initialized,
//! otherwise it shouldn't write at all.
//!
//! It was necessary to create this file because checking if some code write to
//! stdout is very orthogonal to rust's testing tools. There are utilities but
//! only on unstable rust. This file is used to create a binary that can
//! be executed as child process for testing the behavior.

#![allow(unused)]

use std::{net::IpAddr, str::FromStr, time::Duration};

use futures::executor::block_on;
use rustscan::{input::ScanOrder, port_strategy::PortStrategy, scanner::Scanner};

fn main() {
// "open" tcp connection on random port
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
// get the port from above connection
let port = listener.local_addr().unwrap().port();

// execute
block_on(
Scanner::new(
&[IpAddr::from_str("127.0.0.1").unwrap()],
100,
Duration::from_secs(5),
3,
false,
PortStrategy::pick(&None, Some(vec![port]), ScanOrder::Random),
true,
vec![],
false,
)
.run(),
);
}
Loading