mirror of
https://github.com/edu4rdshl/unimap.git
synced 2026-07-17 23:24:49 +00:00
Compare commits
11 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20b6822834 | ||
| c6a06db04b | |||
| a6c166e470 | |||
| cba63c2a04 | |||
| f5049e1074 | |||
| bc08bab228 | |||
| f56b71a008 | |||
| 127d1f0a9a | |||
| d72e115859 | |||
| 482b6f6e21 | |||
| 8b0c7fcb27 |
18 changed files with 2027 additions and 1191 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
/logs
|
/logs
|
||||||
*.txt
|
*.txt
|
||||||
/unimap_logs
|
/unimap_logs
|
||||||
|
/ghbinaries
|
||||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
|
@ -9,7 +9,7 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug",
|
"name": "Debug",
|
||||||
"program": "${workspaceRoot}/target/debug/${workspaceRootFolderName}",
|
"program": "${workspaceRoot}/target/debug/${workspaceRootFolderName}",
|
||||||
"args": "-f targets.txt -u log.csv --resolvers resolvers.txt --iport 1 --lport 10000 --min-rate 10",
|
"args": "-t useast1.sugarcrm.com --fast-scan",
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"sourceLanguages": [
|
"sourceLanguages": [
|
||||||
"rust"
|
"rust"
|
||||||
|
|
|
||||||
2235
Cargo.lock
generated
2235
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
43
Cargo.toml
43
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "unimap"
|
name = "unimap"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
authors = ["Eduard Tolosa <edu4rdshl@protonmail.com>"]
|
authors = ["Eduard Tolosa <edu4rdshl@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Scan only once by IP address and reduce scan times with Nmap for large amounts of data."
|
description = "Scan only once by IP address and reduce scan times with Nmap for large amounts of data."
|
||||||
|
|
@ -12,33 +12,31 @@ readme = "README.md"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
trust-dns-resolver = "0.20.4"
|
hickory-resolver = "0.25.2"
|
||||||
rayon = "1.7.0"
|
rayon = "1.10.0"
|
||||||
log = { version = "0.4.20", features = ["std"] }
|
log = { version = "0.4.27", features = ["std"] }
|
||||||
colored = { version = "2.0.4", optional = true }
|
colored = { version = "3.0.0", optional = true }
|
||||||
rand = "0.8.5"
|
rand = "0.9.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.5.0"
|
||||||
serde = { version = "1.0.186", features = ["derive"] }
|
tokio = { version = "1.47.0", features = ["rt"] }
|
||||||
serde_derive = "1.0.186"
|
futures = "0.3"
|
||||||
prettytable-rs = "0.8.0"
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde-xml-rs = "0.5.1"
|
serde_derive = "1.0.219"
|
||||||
chrono = "0.4.26"
|
prettytable-rs = "0.10.0"
|
||||||
clap = { version = "2.34.0", features = ["yaml"] }
|
serde-xml-rs = "0.8.1"
|
||||||
config = { version = "0.11.0", features = ["yaml", "json", "toml", "hjson", "ini"] }
|
chrono = "0.4.41"
|
||||||
|
clap = { version = "4.5.42", features = ["derive"] }
|
||||||
|
config = { version = "0.15.13", features = ["yaml", "json", "toml", "ini"] }
|
||||||
failure = "0.1.8"
|
failure = "0.1.8"
|
||||||
|
|
||||||
# https://github.com/bluejekyll/trust-dns/pull/1632
|
|
||||||
[patch.crates-io]
|
|
||||||
trust-dns-resolver = { git = "https://github.com/Findomain/trust-dns", package = "trust-dns-resolver", branch = "custombranch" }
|
|
||||||
|
|
||||||
[target.arm-unknown-linux-gnueabihf.dependencies]
|
[target.arm-unknown-linux-gnueabihf.dependencies]
|
||||||
openssl = { version = "0.10.56", features = ["vendored"] }
|
openssl = { version = "0.10.73", features = ["vendored"] }
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-gnu.dependencies]
|
[target.aarch64-unknown-linux-gnu.dependencies]
|
||||||
openssl = { version = "0.10.56", features = ["vendored"] }
|
openssl = { version = "0.10.73", features = ["vendored"] }
|
||||||
|
|
||||||
[target.armv7-unknown-linux-gnueabihf.dependencies]
|
[target.armv7-unknown-linux-gnueabihf.dependencies]
|
||||||
openssl = { version = "0.10.56", features = ["vendored"] }
|
openssl = { version = "0.10.73", features = ["vendored"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
|
|
@ -49,5 +47,4 @@ winapi = { version = "0.3.9", features = ["handleapi", "winbase"] }
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = 'abort'
|
panic = 'abort'
|
||||||
incremental = false
|
strip = true
|
||||||
opt-level = "s"
|
|
||||||
|
|
|
||||||
65
builder.sh
65
builder.sh
|
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Rusolver releaser
|
|
||||||
NAME="unimap"
|
NAME="unimap"
|
||||||
|
|
||||||
LINUX_TARGET="x86_64-unknown-linux-musl"
|
LINUX_TARGET="x86_64-unknown-linux-musl"
|
||||||
|
|
@ -7,24 +6,21 @@ LINUX_X86_TARGET="i686-unknown-linux-musl"
|
||||||
WIN_TARGET="x86_64-pc-windows-gnu"
|
WIN_TARGET="x86_64-pc-windows-gnu"
|
||||||
ARMV7_TARGET="armv7-unknown-linux-gnueabihf"
|
ARMV7_TARGET="armv7-unknown-linux-gnueabihf"
|
||||||
AARCH_TARGET="aarch64-unknown-linux-gnu"
|
AARCH_TARGET="aarch64-unknown-linux-gnu"
|
||||||
OSX_TARGET="x86_64-apple-darwin"
|
# OSX_TARGET="x86_64-apple-darwin"
|
||||||
MANPAGE_DIR="./$NAME.1"
|
MANPAGE_DIR="./$NAME.1"
|
||||||
|
BIN_OUTPUT_DIR="./ghbinaries"
|
||||||
|
|
||||||
if ! systemctl is-active docker >/dev/null 2>&1; then
|
rm -rf "$BIN_OUTPUT_DIR"
|
||||||
echo "Docker is not running. Starting docker."
|
mkdir -p "$BIN_OUTPUT_DIR"
|
||||||
if ! sudo systemctl start docker; then
|
|
||||||
echo "Failed to start docker."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Linux build
|
# Linux build
|
||||||
echo "Building Linux artifact."
|
echo "Building Linux artifact."
|
||||||
if cargo build -q --release --target="$LINUX_TARGET"; then
|
if cross build -q --release --target="$LINUX_TARGET"; then
|
||||||
echo "Linux artifact build: SUCCESS"
|
echo "Linux artifact build: SUCCESS"
|
||||||
cp "target/$LINUX_TARGET/release/$NAME" "target/$LINUX_TARGET/release/$NAME-linux"
|
cp "target/$LINUX_TARGET/release/$NAME" "target/$LINUX_TARGET/release/$NAME-linux"
|
||||||
strip "target/$LINUX_TARGET/release/$NAME-linux"
|
strip "target/$LINUX_TARGET/release/$NAME-linux"
|
||||||
sha512sum "target/$LINUX_TARGET/release/$NAME-linux" >"target/$LINUX_TARGET/release/$NAME-linux.sha512"
|
sha512sum "target/$LINUX_TARGET/release/$NAME-linux" >"$BIN_OUTPUT_DIR/$NAME-linux.sha512"
|
||||||
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-x64.zip" "target/$LINUX_TARGET/release/$NAME-linux"
|
||||||
else
|
else
|
||||||
echo "Linux artifact build: FAILED"
|
echo "Linux artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -35,7 +31,8 @@ if cross build -q --release --target="$LINUX_X86_TARGET"; then
|
||||||
echo "Linux x86 artifact build: SUCCESS"
|
echo "Linux x86 artifact build: SUCCESS"
|
||||||
cp "target/$LINUX_X86_TARGET/release/$NAME" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
cp "target/$LINUX_X86_TARGET/release/$NAME" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
||||||
strip "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
strip "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
||||||
sha512sum "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" >"target/$LINUX_X86_TARGET/release/$NAME-linux-i386.sha512"
|
sha512sum "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" >"$BIN_OUTPUT_DIR/$NAME-linux-i386.sha512"
|
||||||
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-i386.zip" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
||||||
else
|
else
|
||||||
echo "Linux x86 artifact build: FAILED"
|
echo "Linux x86 artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -46,7 +43,8 @@ if cross build -q --release --target="$WIN_TARGET"; then
|
||||||
echo "Windows artifact build: SUCCESS"
|
echo "Windows artifact build: SUCCESS"
|
||||||
cp "target/$WIN_TARGET/release/$NAME.exe" "target/$WIN_TARGET/release/$NAME-windows.exe"
|
cp "target/$WIN_TARGET/release/$NAME.exe" "target/$WIN_TARGET/release/$NAME-windows.exe"
|
||||||
strip "target/$WIN_TARGET/release/$NAME-windows.exe"
|
strip "target/$WIN_TARGET/release/$NAME-windows.exe"
|
||||||
sha512sum "target/$WIN_TARGET/release/$NAME-windows.exe" >"target/$WIN_TARGET/release/$NAME-windows.exe.sha512"
|
sha512sum "target/$WIN_TARGET/release/$NAME-windows.exe" >"$BIN_OUTPUT_DIR/$NAME-windows.exe.sha512"
|
||||||
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-windows.zip" "target/$WIN_TARGET/release/$NAME-windows.exe"
|
||||||
else
|
else
|
||||||
echo "Windows artifact build: FAILED"
|
echo "Windows artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -57,7 +55,8 @@ if cross build -q --release --target="$ARMV7_TARGET"; then
|
||||||
echo "ARMv7 artifact build: SUCCESS"
|
echo "ARMv7 artifact build: SUCCESS"
|
||||||
cp "target/$ARMV7_TARGET/release/$NAME" "target/$ARMV7_TARGET/release/$NAME-armv7"
|
cp "target/$ARMV7_TARGET/release/$NAME" "target/$ARMV7_TARGET/release/$NAME-armv7"
|
||||||
strip "target/$ARMV7_TARGET/release/$NAME-armv7"
|
strip "target/$ARMV7_TARGET/release/$NAME-armv7"
|
||||||
sha512sum "target/$ARMV7_TARGET/release/$NAME-armv7" >"target/$ARMV7_TARGET/release/$NAME-armv7.sha512"
|
sha512sum "target/$ARMV7_TARGET/release/$NAME-armv7" >"$BIN_OUTPUT_DIR/$NAME-armv7.sha512"
|
||||||
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-armv7.zip" "target/$ARMV7_TARGET/release/$NAME-armv7"
|
||||||
else
|
else
|
||||||
echo "ARMv7 artifact build: FAILED"
|
echo "ARMv7 artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -68,41 +67,31 @@ if cross build -q --release --target="$AARCH_TARGET"; then
|
||||||
echo "Aarch64 artifact build: SUCCESS"
|
echo "Aarch64 artifact build: SUCCESS"
|
||||||
cp "target/$AARCH_TARGET/release/$NAME" "target/$AARCH_TARGET/release/$NAME-aarch64"
|
cp "target/$AARCH_TARGET/release/$NAME" "target/$AARCH_TARGET/release/$NAME-aarch64"
|
||||||
strip "target/$AARCH_TARGET/release/$NAME-aarch64"
|
strip "target/$AARCH_TARGET/release/$NAME-aarch64"
|
||||||
|
sha512sum "target/$AARCH_TARGET/release/$NAME-aarch64" >"$BIN_OUTPUT_DIR/$NAME-aarch64.sha512"
|
||||||
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-aarch64.zip" "target/$AARCH_TARGET/release/$NAME-aarch64"
|
||||||
else
|
else
|
||||||
echo "Aarch64 artifact build: FAILED"
|
echo "Aarch64 artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Mac OS build
|
# # Mac OS build
|
||||||
echo "Building OSX artifact."
|
# echo "Building OSX artifact."
|
||||||
if CC=o64-clang CXX=o64-clang++ LIBZ_SYS_STATIC=1 cargo build -q --release --target="$OSX_TARGET"; then
|
# if CC=o64-clang CXX=o64-clang++ LIBZ_SYS_STATIC=1 cargo build -q --release --target="$OSX_TARGET"; then
|
||||||
echo "OSX artifact build: SUCCESS"
|
# echo "OSX artifact build: SUCCESS"
|
||||||
cp "target/$OSX_TARGET/release/$NAME" "target/$OSX_TARGET/release/$NAME-osx"
|
# cp "target/$OSX_TARGET/release/$NAME" "target/$OSX_TARGET/release/$NAME-osx"
|
||||||
strip "target/$OSX_TARGET/release/$NAME-osx"
|
# strip "target/$OSX_TARGET/release/$NAME-osx"
|
||||||
sha512sum "target/$OSX_TARGET/release/$NAME-osx" >"target/$OSX_TARGET/release/$NAME-osx.sha512"
|
# sha512sum "target/$OSX_TARGET/release/$NAME-osx" >"target/$OSX_TARGET/release/$NAME-osx.sha512"
|
||||||
else
|
# zip -q "$BIN_OUTPUT_DIR/$NAME-osx.zip" "target/$OSX_TARGET/release/$NAME-osx"
|
||||||
echo "OSX artifact build: FAILED"
|
# else
|
||||||
fi
|
# echo "OSX artifact build: FAILED"
|
||||||
|
# fi
|
||||||
|
|
||||||
echo "Creating manpage..."
|
echo "Creating manpage..."
|
||||||
if command -v help2man >/dev/null; then
|
if command -v help2man >/dev/null; then
|
||||||
if help2man -o "$MANPAGE_DIR" "target/$LINUX_TARGET/release/$NAME"; then
|
if help2man -o "$MANPAGE_DIR" "target/$LINUX_TARGET/release/$NAME"; then
|
||||||
echo "Manpage created sucessfully and saved in $MANPAGE_DIR"
|
echo "Manpage created successfully and saved in $MANPAGE_DIR"
|
||||||
else
|
else
|
||||||
echo "Error creating manpage."
|
echo "Error creating manpage."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Please install the help2man package."
|
echo "Please install the help2man package."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v git >/dev/null; then
|
|
||||||
git add .
|
|
||||||
git commit -m "Bump version."
|
|
||||||
git push
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Stop docker
|
|
||||||
echo "Stopping docker."
|
|
||||||
if ! sudo systemctl stop docker; then
|
|
||||||
echo "Failed to stop docker."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
|
||||||
201
src/args.rs
201
src/args.rs
|
|
@ -1,72 +1,167 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{defaults, logic::validate_target, misc::sanitize_target_string},
|
||||||
defaults,
|
|
||||||
logic::validate_target,
|
|
||||||
misc::{return_matches_vec, sanitize_target_string},
|
|
||||||
structs::Args,
|
|
||||||
},
|
|
||||||
chrono::Utc,
|
chrono::Utc,
|
||||||
clap::{load_yaml, value_t, App},
|
clap::Parser,
|
||||||
std::{collections::HashSet, time::Instant},
|
std::{collections::HashSet, time::Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
/// Scan only once by IP address and reduce scan times with Nmap for large amounts of data.
|
||||||
pub fn get_args() -> Args {
|
#[derive(Parser, Debug, Clone)]
|
||||||
let yaml = load_yaml!("cli.yml");
|
#[command(author = "Eduard Tolosa <edu4rdshl@protonmail.com>", version, about, long_about = None)]
|
||||||
let matches = App::from_yaml(yaml)
|
#[command(arg_required_else_help = true)]
|
||||||
.version(clap::crate_version!())
|
pub struct Args {
|
||||||
.get_matches();
|
/// Target host
|
||||||
|
#[arg(short, long, conflicts_with_all = ["files", "stdin"])]
|
||||||
|
pub target: Option<String>,
|
||||||
|
|
||||||
Args {
|
/// Use a list of targets written in a file as input
|
||||||
target: {
|
#[arg(short, long, conflicts_with_all = ["target", "stdin"])]
|
||||||
let target = sanitize_target_string(
|
pub files: Vec<String>,
|
||||||
value_t!(matches, "target", String).unwrap_or_else(|_| String::new()),
|
|
||||||
);
|
/// Write to an output file. The name of the output file will be unimap-log-date
|
||||||
if validate_target(&target) {
|
#[arg(short, long, conflicts_with = "unique_output")]
|
||||||
target
|
pub output: bool,
|
||||||
|
|
||||||
|
/// Write the output in CSV format to the specified filename
|
||||||
|
#[arg(short, long, conflicts_with = "output")]
|
||||||
|
pub unique_output: Option<String>,
|
||||||
|
|
||||||
|
/// Remove informative messages
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub quiet: bool,
|
||||||
|
|
||||||
|
/// Number of threads to use to perform the resolution
|
||||||
|
#[arg(long)]
|
||||||
|
pub threads: Option<usize>,
|
||||||
|
|
||||||
|
/// Path to a file (or files) containing a list of DNS IP address. If no specified then a list of built-in DNS servers is used
|
||||||
|
#[arg(long = "resolvers")]
|
||||||
|
pub custom_resolvers: Vec<String>,
|
||||||
|
|
||||||
|
/// Ports to scan. You can specify a range of ports, a list, or both. Put them inside double quotes, for example: "22, 80, 443, 1000-5000"
|
||||||
|
#[arg(long)]
|
||||||
|
pub ports: Option<String>,
|
||||||
|
|
||||||
|
/// Nmap --min-rate value for ports scan
|
||||||
|
#[arg(long)]
|
||||||
|
pub min_rate: Option<String>,
|
||||||
|
|
||||||
|
/// Use fast scanning for ports (no version detection)
|
||||||
|
#[arg(long)]
|
||||||
|
pub fast_scan: bool,
|
||||||
|
|
||||||
|
/// Path to save the CSV data of the process and/or Nmap XML files. Default to logs/
|
||||||
|
#[arg(long, default_value = "unimap_logs")]
|
||||||
|
pub logs_dir: String,
|
||||||
|
|
||||||
|
/// Keep Nmap XML files created in the logs/ folder for every scanned IP. This data will be useful for other tasks
|
||||||
|
#[arg(short = 'k', long)]
|
||||||
|
pub no_keep_nmap_logs: bool,
|
||||||
|
|
||||||
|
/// Use raw output instead of a table
|
||||||
|
#[arg(short, long, conflicts_with = "url_output")]
|
||||||
|
pub raw_output: bool,
|
||||||
|
|
||||||
|
/// Use HOST:IP output format
|
||||||
|
#[arg(long, conflicts_with = "raw_output")]
|
||||||
|
pub url_output: bool,
|
||||||
|
|
||||||
|
/// Read from stdin instead of files or arguments
|
||||||
|
#[arg(long, conflicts_with_all = ["files", "target"])]
|
||||||
|
pub stdin: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Args {
|
||||||
|
/// Create the processed args with computed fields
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_processed_args(self) -> ProcessedArgs {
|
||||||
|
let target = self.target.map_or_else(String::new, |target| {
|
||||||
|
let sanitized = sanitize_target_string(target);
|
||||||
|
if validate_target(&sanitized) {
|
||||||
|
sanitized
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
file_name: if matches.is_present("output") {
|
|
||||||
value_t!(matches, "logs-dir", String).unwrap_or_else(|_| "unimap_logs".to_string())
|
let file_name = if self.output {
|
||||||
+ "/"
|
format!(
|
||||||
+ "unimap"
|
"{}/unimap{}",
|
||||||
+ &Utc::now().format("-log-%Y-%m-%d_%H-%M-%S").to_string()
|
self.logs_dir,
|
||||||
+ ".csv"
|
Utc::now().format("-log-%Y-%m-%d_%H-%M-%S")
|
||||||
} else if matches.is_present("unique-output") {
|
) + ".csv"
|
||||||
matches.value_of("unique-output").unwrap().to_string()
|
} else if let Some(unique_output) = &self.unique_output {
|
||||||
|
unique_output.clone()
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
},
|
};
|
||||||
logs_dir: value_t!(matches, "logs-dir", String)
|
|
||||||
.unwrap_or_else(|_| "unimap_logs".to_string()),
|
let threads = if self.ports.is_some() && self.threads.is_none() {
|
||||||
threads: if matches.is_present("ports") && !matches.is_present("threads") {
|
|
||||||
30
|
30
|
||||||
} else {
|
} else {
|
||||||
value_t!(matches, "threads", usize).unwrap_or_else(|_| 50)
|
self.threads.unwrap_or(50)
|
||||||
},
|
};
|
||||||
version: clap::crate_version!().to_string(),
|
|
||||||
ports: value_t!(matches, "ports", String).unwrap_or_else(|_| String::new()),
|
let custom_resolvers_flag = !self.custom_resolvers.is_empty();
|
||||||
with_output: matches.is_present("output") || matches.is_present("unique-output"),
|
let custom_ports_range = self.ports.is_some();
|
||||||
unique_output_flag: matches.is_present("unique-output"),
|
let ports_value = self.ports.unwrap_or_default();
|
||||||
from_file_flag: matches.is_present("files"),
|
|
||||||
quiet_flag: matches.is_present("quiet"),
|
let resolvers = if custom_resolvers_flag {
|
||||||
custom_resolvers: matches.is_present("custom-resolvers"),
|
self.custom_resolvers
|
||||||
custom_ports_range: matches.is_present("ports"),
|
|
||||||
fast_scan: matches.is_present("fast-scan"),
|
|
||||||
no_keep_nmap_logs: matches.is_present("no-keep-nmap-logs"),
|
|
||||||
raw_output: matches.is_present("raw-output"),
|
|
||||||
url_output: matches.is_present("url-output"),
|
|
||||||
from_stdin: matches.is_present("stdin"),
|
|
||||||
files: return_matches_vec(&matches, "files"),
|
|
||||||
min_rate: value_t!(matches, "min-rate", String).unwrap_or_else(|_| String::new()),
|
|
||||||
resolvers: if matches.is_present("custom-resolvers") {
|
|
||||||
return_matches_vec(&matches, "custom-resolvers")
|
|
||||||
} else {
|
} else {
|
||||||
defaults::ipv4_resolvers()
|
defaults::ipv4_resolvers()
|
||||||
},
|
};
|
||||||
|
|
||||||
|
ProcessedArgs {
|
||||||
|
target,
|
||||||
|
file_name,
|
||||||
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
logs_dir: self.logs_dir,
|
||||||
|
threads,
|
||||||
|
ports: ports_value,
|
||||||
|
with_output: self.output || self.unique_output.is_some(),
|
||||||
|
unique_output_flag: self.unique_output.is_some(),
|
||||||
|
min_rate: self.min_rate.unwrap_or_default(),
|
||||||
|
from_file_flag: !self.files.is_empty(),
|
||||||
|
quiet_flag: self.quiet,
|
||||||
|
custom_resolvers: custom_resolvers_flag,
|
||||||
|
custom_ports_range,
|
||||||
|
no_keep_nmap_logs: self.no_keep_nmap_logs,
|
||||||
|
raw_output: self.raw_output,
|
||||||
|
fast_scan: self.fast_scan,
|
||||||
|
url_output: self.url_output,
|
||||||
|
from_stdin: self.stdin,
|
||||||
|
files: self.files,
|
||||||
|
resolvers,
|
||||||
targets: HashSet::new(),
|
targets: HashSet::new(),
|
||||||
time_wasted: Instant::now(),
|
time_wasted: Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processed args with computed fields and flags
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ProcessedArgs {
|
||||||
|
pub target: String,
|
||||||
|
pub file_name: String,
|
||||||
|
pub version: String,
|
||||||
|
pub logs_dir: String,
|
||||||
|
pub threads: usize,
|
||||||
|
pub ports: String,
|
||||||
|
pub with_output: bool,
|
||||||
|
pub unique_output_flag: bool,
|
||||||
|
pub min_rate: String,
|
||||||
|
pub from_file_flag: bool,
|
||||||
|
pub quiet_flag: bool,
|
||||||
|
pub custom_resolvers: bool,
|
||||||
|
pub custom_ports_range: bool,
|
||||||
|
pub no_keep_nmap_logs: bool,
|
||||||
|
pub raw_output: bool,
|
||||||
|
pub fast_scan: bool,
|
||||||
|
pub url_output: bool,
|
||||||
|
pub from_stdin: bool,
|
||||||
|
pub files: Vec<String>,
|
||||||
|
pub resolvers: Vec<String>,
|
||||||
|
pub targets: HashSet<String>,
|
||||||
|
pub time_wasted: Instant,
|
||||||
|
}
|
||||||
|
|
|
||||||
113
src/cli.yml
113
src/cli.yml
|
|
@ -1,113 +0,0 @@
|
||||||
name: Unimap
|
|
||||||
author: Eduard Tolosa <edu4rdshl@protonmail.com>
|
|
||||||
settings:
|
|
||||||
- ArgRequiredElseHelp
|
|
||||||
- StrictUtf8
|
|
||||||
about: Scan only once by IP address and reduce scan times with Nmap for large amounts of data.
|
|
||||||
args:
|
|
||||||
- target:
|
|
||||||
short: t
|
|
||||||
long: target
|
|
||||||
help: Target host.
|
|
||||||
takes_value: true
|
|
||||||
multiple: false
|
|
||||||
conflicts_with:
|
|
||||||
- files
|
|
||||||
- stdin
|
|
||||||
|
|
||||||
- files:
|
|
||||||
short: f
|
|
||||||
long: file
|
|
||||||
help: Use a list of targets writen in a file as input.
|
|
||||||
takes_value: true
|
|
||||||
multiple: true
|
|
||||||
conflicts_with:
|
|
||||||
- target
|
|
||||||
- stdin
|
|
||||||
|
|
||||||
- output:
|
|
||||||
short: o
|
|
||||||
long: output
|
|
||||||
help: Write to an output file. The name of the output file will be unimap-log-date.
|
|
||||||
takes_value: false
|
|
||||||
|
|
||||||
- unique-output:
|
|
||||||
short: u
|
|
||||||
long: unique-output
|
|
||||||
help: Write the output in CSV format to the specified filename.
|
|
||||||
takes_value: true
|
|
||||||
multiple: false
|
|
||||||
conflicts_with:
|
|
||||||
- output
|
|
||||||
|
|
||||||
- quiet:
|
|
||||||
short: q
|
|
||||||
long: quiet
|
|
||||||
help: Remove informative messages.
|
|
||||||
takes_value: false
|
|
||||||
|
|
||||||
- threads:
|
|
||||||
help: Number of threads to use to perform the resolution.
|
|
||||||
long: threads
|
|
||||||
takes_value: true
|
|
||||||
|
|
||||||
- custom-resolvers:
|
|
||||||
help: Path to a file (or files) containing a list of DNS IP address. If no specified then 1.6k of built-in valid DNS servers from public-dns.info are used.
|
|
||||||
long: resolvers
|
|
||||||
takes_value: true
|
|
||||||
multiple: true
|
|
||||||
|
|
||||||
- ports:
|
|
||||||
help: 'Ports to scan. You can specify a range of ports, a list, or both. Put them inside double quotes, for example: "22, 80, 443, 1000-5000"'
|
|
||||||
long: ports
|
|
||||||
takes_value: true
|
|
||||||
multiple: false
|
|
||||||
|
|
||||||
- min-rate:
|
|
||||||
help: Nmap --min-rate value for ports scan.
|
|
||||||
long: min-rate
|
|
||||||
takes_value: true
|
|
||||||
|
|
||||||
- fast-scan:
|
|
||||||
help: Use fast scanning for ports (no version detection).
|
|
||||||
long: fast-scan
|
|
||||||
takes_value: false
|
|
||||||
|
|
||||||
- logs-dir:
|
|
||||||
help: Path to save the CSV data of the process and/or Nmap XML files. Default to logs/.
|
|
||||||
long: logs-dir
|
|
||||||
takes_value: true
|
|
||||||
multiple: false
|
|
||||||
|
|
||||||
- no-keep-nmap-logs:
|
|
||||||
help: Keep Nmap XML files created in the logs/ folder for every scanned IP. This data will be useful for other tasks.
|
|
||||||
short: k
|
|
||||||
long: no-keep-nmap-logs
|
|
||||||
takes_value: false
|
|
||||||
multiple: false
|
|
||||||
|
|
||||||
- raw-output:
|
|
||||||
help: Use raw output instead of a table.
|
|
||||||
short: r
|
|
||||||
long: raw-output
|
|
||||||
takes_value: false
|
|
||||||
multiple: false
|
|
||||||
conflicts_with:
|
|
||||||
- url-output
|
|
||||||
|
|
||||||
- url-output:
|
|
||||||
help: Use HOST:IP output format.
|
|
||||||
long: url-output
|
|
||||||
takes_value: false
|
|
||||||
multiple: false
|
|
||||||
conflicts_with:
|
|
||||||
- raw-output
|
|
||||||
|
|
||||||
- stdin:
|
|
||||||
help: Read from stdin instead of files or aguments.
|
|
||||||
long: stdin
|
|
||||||
takes_value: false
|
|
||||||
multiple: false
|
|
||||||
conflicts_with:
|
|
||||||
- files
|
|
||||||
- target
|
|
||||||
|
|
@ -16,12 +16,6 @@ pub fn ipv4_resolvers() -> Vec<String> {
|
||||||
// Verisign
|
// Verisign
|
||||||
"64.6.64.6",
|
"64.6.64.6",
|
||||||
"64.6.65.6",
|
"64.6.65.6",
|
||||||
// UncensoredDNS
|
|
||||||
"91.239.100.100",
|
|
||||||
"89.233.43.71",
|
|
||||||
// dns.watch
|
|
||||||
"84.200.69.80",
|
|
||||||
"84.200.70.40",
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(str::to_owned)
|
.map(str::to_owned)
|
||||||
|
|
|
||||||
30
src/files.rs
30
src/files.rs
|
|
@ -1,5 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::{errors::*, structs::Args},
|
crate::{
|
||||||
|
args::ProcessedArgs,
|
||||||
|
errors::{Result, ResultExt},
|
||||||
|
},
|
||||||
log::error,
|
log::error,
|
||||||
prettytable::Table,
|
prettytable::Table,
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -10,26 +13,27 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn return_file_targets(args: &Args, mut files: Vec<String>) -> Vec<String> {
|
#[must_use]
|
||||||
|
pub fn return_file_targets(args: &ProcessedArgs, mut files: Vec<String>) -> Vec<String> {
|
||||||
let mut targets: Vec<String> = Vec::new();
|
let mut targets: Vec<String> = Vec::new();
|
||||||
files.sort();
|
files.sort();
|
||||||
files.dedup();
|
files.dedup();
|
||||||
for f in files {
|
for f in files {
|
||||||
match File::open(&f) {
|
match File::open(&f) {
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
for target in BufReader::new(file).lines().flatten() {
|
for target in BufReader::new(file)
|
||||||
|
.lines()
|
||||||
|
.map_while(std::result::Result::ok)
|
||||||
|
{
|
||||||
targets.push(target);
|
targets.push(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if args.files.len() == 1 {
|
if args.files.len() == 1 {
|
||||||
error!("Can not open file {}. Error: {}\n", f, e);
|
error!("Can not open file {f}. Error: {e}\n");
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
} else if !args.quiet_flag {
|
} else if !args.quiet_flag {
|
||||||
error!(
|
error!("Can not open file {f}, working with next file. Error: {e}\n");
|
||||||
"Can not open file {}, working with next file. Error: {}\n",
|
|
||||||
f, e
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +48,8 @@ pub fn table_to_file(table: &Table, file_name: Option<std::fs::File>) -> Result<
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_output_file(args: &Args) -> Option<File> {
|
#[must_use]
|
||||||
|
pub fn return_output_file(args: &ProcessedArgs) -> Option<File> {
|
||||||
if args.file_name.is_empty() || !args.with_output {
|
if args.file_name.is_empty() || !args.with_output {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -59,23 +64,24 @@ pub fn return_output_file(args: &Args) -> Option<File> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn check_full_path(full_path: &str) -> bool {
|
pub fn check_full_path(full_path: &str) -> bool {
|
||||||
(Path::new(full_path).exists() && Path::new(full_path).is_dir())
|
(Path::new(full_path).exists() && Path::new(full_path).is_dir())
|
||||||
|| fs::create_dir_all(full_path).is_ok()
|
|| fs::create_dir_all(full_path).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_files(paths: &HashSet<String>) {
|
pub fn delete_files<S: ::std::hash::BuildHasher>(paths: &HashSet<String, S>) {
|
||||||
for file in paths {
|
for file in paths {
|
||||||
if Path::new(&file).exists() {
|
if Path::new(&file).exists() {
|
||||||
match std::fs::remove_file(file) {
|
match std::fs::remove_file(file) {
|
||||||
Ok(_) => (),
|
Ok(()) => (),
|
||||||
Err(e) => error!("Error deleting the file {}. Description: {}", &file, e),
|
Err(e) => error!("Error deleting the file {}. Description: {}", &file, e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string_to_file(data: String, mut file: File) -> Result<()> {
|
pub fn string_to_file(data: &str, mut file: File) -> Result<()> {
|
||||||
file.write_all(data.as_bytes())?;
|
file.write_all(data.as_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
extern crate atty;
|
extern crate atty;
|
||||||
#[cfg(feature = "chrono")]
|
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
#[cfg(feature = "colored")]
|
#[cfg(feature = "colored")]
|
||||||
extern crate colored;
|
extern crate colored;
|
||||||
|
|
@ -8,7 +7,6 @@ extern crate log;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
#[cfg(feature = "colored")]
|
#[cfg(feature = "colored")]
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
@ -23,10 +21,10 @@ impl Log for SimpleLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&self, record: &Record) {
|
fn log(&self, record: &Record) {
|
||||||
let target = if !record.target().is_empty() {
|
let target = if record.target().is_empty() {
|
||||||
record.target()
|
|
||||||
} else {
|
|
||||||
record.module_path().unwrap_or_default()
|
record.module_path().unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
record.target()
|
||||||
};
|
};
|
||||||
if target.contains("unimap") && self.enabled(record.metadata()) {
|
if target.contains("unimap") && self.enabled(record.metadata()) {
|
||||||
let level_string = {
|
let level_string = {
|
||||||
|
|
@ -45,7 +43,6 @@ impl Log for SimpleLogger {
|
||||||
record.level().to_string()
|
record.level().to_string()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#[cfg(feature = "chrono")]
|
|
||||||
{
|
{
|
||||||
print!(
|
print!(
|
||||||
"\n{} [{}] {}",
|
"\n{} [{}] {}",
|
||||||
|
|
@ -54,10 +51,6 @@ impl Log for SimpleLogger {
|
||||||
record.args()
|
record.args()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "chrono"))]
|
|
||||||
{
|
|
||||||
print!("\n[{}] {}", level_string, record.args());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pub fn validate_target(target: &str) -> bool {
|
||||||
!target.starts_with('.')
|
!target.starts_with('.')
|
||||||
&& target.contains('.')
|
&& target.contains('.')
|
||||||
&& !target.contains(&SPECIAL_CHARS[..])
|
&& !target.contains(&SPECIAL_CHARS[..])
|
||||||
&& target.chars().all(|c| c.is_ascii())
|
&& target.is_ascii()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn null_ip_checker(ip: &str) -> String {
|
pub fn null_ip_checker(ip: &str) -> String {
|
||||||
|
|
|
||||||
27
src/main.rs
27
src/main.rs
|
|
@ -1,27 +1,30 @@
|
||||||
use {
|
use {
|
||||||
|
clap::Parser,
|
||||||
log::{error, Level},
|
log::{error, Level},
|
||||||
std::{collections::HashSet, iter::FromIterator},
|
std::{collections::HashSet, iter::FromIterator},
|
||||||
unimap::{args, errors::*, files::return_file_targets, logger, misc, resolver_engine},
|
unimap::{args, errors::Result, files::return_file_targets, logger, misc, resolver_engine},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn run() -> Result<()> {
|
fn run() -> Result<()> {
|
||||||
if std::env::var("UNIMAP_LOG_LEVEL").is_ok() {
|
if std::env::var("UNIMAP_LOG_LEVEL").is_ok() {
|
||||||
logger::init_by_env()
|
logger::init_by_env();
|
||||||
} else {
|
} else {
|
||||||
logger::init_with_level(Level::Info).unwrap()
|
logger::init_with_level(Level::Info).unwrap();
|
||||||
}
|
}
|
||||||
let mut arguments = args::get_args();
|
|
||||||
|
let mut arguments = args::Args::parse().into_processed_args();
|
||||||
|
|
||||||
if !arguments.files.is_empty() {
|
if !arguments.files.is_empty() {
|
||||||
arguments.targets =
|
arguments.targets =
|
||||||
HashSet::from_iter(return_file_targets(&arguments, arguments.files.clone()))
|
HashSet::from_iter(return_file_targets(&arguments, arguments.files.clone()));
|
||||||
} else if !arguments.target.is_empty() {
|
} else if !arguments.target.is_empty() {
|
||||||
arguments.targets.insert(arguments.target.clone());
|
arguments.targets.insert(arguments.target.clone());
|
||||||
} else {
|
} else {
|
||||||
arguments.targets = misc::read_stdin()
|
arguments.targets = misc::read_stdin();
|
||||||
}
|
}
|
||||||
|
|
||||||
if arguments.targets.len() < 50 {
|
if arguments.targets.len() < 50 {
|
||||||
arguments.threads = arguments.targets.len()
|
arguments.threads = arguments.targets.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
rayon::ThreadPoolBuilder::new()
|
rayon::ThreadPoolBuilder::new()
|
||||||
|
|
@ -29,19 +32,19 @@ fn run() -> Result<()> {
|
||||||
.build_global()
|
.build_global()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if !arguments.targets.is_empty() {
|
if arguments.targets.is_empty() {
|
||||||
resolver_engine::parallel_resolver_all(&mut arguments)
|
|
||||||
} else {
|
|
||||||
error!("Error: Target is empty or invalid!\n");
|
error!("Error: Target is empty or invalid!\n");
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
|
} else {
|
||||||
|
resolver_engine::parallel_resolver_all(&mut arguments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = run() {
|
if let Err(err) = run() {
|
||||||
error!("Error: {}", err);
|
error!("Error: {err}");
|
||||||
for cause in err.iter_chain().skip(1) {
|
for cause in err.iter_chain().skip(1) {
|
||||||
error!("Error description: {}\n", cause);
|
error!("Error description: {cause}\n");
|
||||||
}
|
}
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
src/misc.rs
26
src/misc.rs
|
|
@ -3,6 +3,7 @@ use std::{
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn sanitize_target_string(target: String) -> String {
|
pub fn sanitize_target_string(target: String) -> String {
|
||||||
target
|
target
|
||||||
.replace("www.", "")
|
.replace("www.", "")
|
||||||
|
|
@ -11,31 +12,6 @@ pub fn sanitize_target_string(target: String) -> String {
|
||||||
.replace('/', "")
|
.replace('/', "")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_matches_vec(matches: &clap::ArgMatches, value: &str) -> Vec<String> {
|
|
||||||
if matches.is_present(value) {
|
|
||||||
matches
|
|
||||||
.values_of(value)
|
|
||||||
.unwrap()
|
|
||||||
.map(str::to_owned)
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn return_matches_hashset(matches: &clap::ArgMatches, value: &str) -> HashSet<String> {
|
|
||||||
if matches.is_present(value) {
|
|
||||||
matches
|
|
||||||
.values_of(value)
|
|
||||||
.unwrap()
|
|
||||||
.map(str::to_owned)
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
HashSet::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_stdin() -> HashSet<String> {
|
pub fn read_stdin() -> HashSet<String> {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
let mut stdin = io::stdin();
|
let mut stdin = io::stdin();
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,43 @@
|
||||||
use {
|
use {
|
||||||
crate::{files, structs::Args},
|
crate::{args::ProcessedArgs, files},
|
||||||
std::{collections::HashSet, net::SocketAddr},
|
hickory_resolver::{
|
||||||
trust_dns_resolver::{
|
config::{NameServerConfig, NameServerConfigGroup, ResolverConfig, ResolverOpts},
|
||||||
config::{NameServerConfig, NameServerConfigGroup, Protocol, ResolverConfig, ResolverOpts},
|
name_server::TokioConnectionProvider,
|
||||||
Resolver,
|
proto::xfer::Protocol,
|
||||||
|
TokioResolver,
|
||||||
},
|
},
|
||||||
|
std::{collections::HashSet, net::SocketAddr},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get_records(resolver: &Resolver, domain: &str) -> String {
|
pub fn get_records(resolver: &TokioResolver, domain: &str) -> String {
|
||||||
if let Ok(ips) = resolver.ipv4_lookup(domain) {
|
futures::executor::block_on(resolver.ipv4_lookup(domain)).map_or_else(
|
||||||
|
|_| String::new(),
|
||||||
|
|ips| {
|
||||||
ips.iter()
|
ips.iter()
|
||||||
.map(|x| x.to_string())
|
.map(std::string::ToString::to_string)
|
||||||
.next()
|
.next()
|
||||||
.expect("Failed to get IPV4.")
|
.expect("Failed to get IPV4.")
|
||||||
} else {
|
},
|
||||||
String::new()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_resolver(nameserver_ips: HashSet<SocketAddr>, opts: ResolverOpts) -> Resolver {
|
pub fn get_resolver(nameserver_ips: HashSet<SocketAddr>, opts: ResolverOpts) -> TokioResolver {
|
||||||
let mut name_servers = NameServerConfigGroup::with_capacity(nameserver_ips.len() * 2);
|
let mut name_servers = NameServerConfigGroup::with_capacity(nameserver_ips.len());
|
||||||
name_servers.extend(nameserver_ips.into_iter().flat_map(|socket_addr| {
|
name_servers.extend(
|
||||||
std::iter::once(NameServerConfig {
|
nameserver_ips
|
||||||
socket_addr,
|
.into_iter()
|
||||||
protocol: Protocol::Udp,
|
.map(|socket_addr| NameServerConfig::new(socket_addr, Protocol::Udp)),
|
||||||
tls_dns_name: None,
|
);
|
||||||
trust_nx_responses: false,
|
|
||||||
})
|
TokioResolver::builder_with_config(
|
||||||
.chain(std::iter::once(NameServerConfig {
|
ResolverConfig::from_parts(None, vec![], name_servers),
|
||||||
socket_addr,
|
TokioConnectionProvider::default(),
|
||||||
protocol: Protocol::Tcp,
|
)
|
||||||
tls_dns_name: None,
|
.with_options(opts)
|
||||||
trust_nx_responses: false,
|
.build()
|
||||||
}))
|
|
||||||
}));
|
|
||||||
Resolver::new(ResolverConfig::from_parts(None, vec![], name_servers), opts).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_socket_address(args: &Args) -> HashSet<SocketAddr> {
|
pub fn return_socket_address(args: &ProcessedArgs) -> HashSet<SocketAddr> {
|
||||||
let mut resolver_ips = HashSet::new();
|
let mut resolver_ips = HashSet::new();
|
||||||
if args.custom_resolvers {
|
if args.custom_resolvers {
|
||||||
for r in &files::return_file_targets(args, args.resolvers.clone()) {
|
for r in &files::return_file_targets(args, args.resolvers.clone()) {
|
||||||
|
|
|
||||||
32
src/nmap.rs
32
src/nmap.rs
|
|
@ -1,18 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::resolver_engine,
|
|
||||||
log::error,
|
log::error,
|
||||||
std::{path::Path, process::Command},
|
std::{net::Ipv4Addr, path::Path, process::Command},
|
||||||
};
|
};
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref NMAP_DNS_RESOLVERS: String = resolver_engine::RESOLVERS
|
|
||||||
.clone()
|
|
||||||
.iter()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
|
|
@ -114,12 +104,19 @@ pub fn get_nmap_data(
|
||||||
min_rate: &str,
|
min_rate: &str,
|
||||||
ports: &str,
|
ports: &str,
|
||||||
fast_scan: bool,
|
fast_scan: bool,
|
||||||
|
resolvers: &[Ipv4Addr],
|
||||||
) -> Result<Nmaprun, serde_xml_rs::Error> {
|
) -> Result<Nmaprun, serde_xml_rs::Error> {
|
||||||
let min_rate = min_rate.to_string();
|
let min_rate = min_rate.to_string();
|
||||||
|
let nmap_dns_resolvers = resolvers
|
||||||
|
.iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(",");
|
||||||
|
|
||||||
let mut nmap_args = vec![
|
let mut nmap_args = vec![
|
||||||
"nmap",
|
"nmap",
|
||||||
"--dns-servers",
|
"--dns-servers",
|
||||||
&NMAP_DNS_RESOLVERS,
|
&nmap_dns_resolvers,
|
||||||
"-Pn",
|
"-Pn",
|
||||||
"-sS",
|
"-sS",
|
||||||
"--open",
|
"--open",
|
||||||
|
|
@ -132,17 +129,17 @@ pub fn get_nmap_data(
|
||||||
];
|
];
|
||||||
|
|
||||||
if !min_rate.is_empty() {
|
if !min_rate.is_empty() {
|
||||||
nmap_args.append(&mut vec!["--min-rate", &min_rate])
|
nmap_args.append(&mut vec!["--min-rate", &min_rate]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if fast_scan {
|
if fast_scan {
|
||||||
nmap_args.append(&mut vec!["--host-timeout", "20m"])
|
nmap_args.append(&mut vec!["--host-timeout", "20m"]);
|
||||||
} else {
|
} else {
|
||||||
nmap_args.append(&mut vec!["-sV"])
|
nmap_args.append(&mut vec!["-sV"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ports.is_empty() {
|
if !ports.is_empty() {
|
||||||
nmap_args.append(&mut vec!["-p", ports])
|
nmap_args.append(&mut vec!["-p", ports]);
|
||||||
}
|
}
|
||||||
|
|
||||||
nmap_args.push(host);
|
nmap_args.push(host);
|
||||||
|
|
@ -159,8 +156,7 @@ pub fn get_nmap_data(
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
"Error waiting command to finish for {}, continuing with remaining hosts. Description: {}",
|
"Error waiting command to finish for {host}, continuing with remaining hosts. Description: {e}"
|
||||||
host, e
|
|
||||||
);
|
);
|
||||||
Ok(Nmaprun::default())
|
Ok(Nmaprun::default())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
args,
|
args::ProcessedArgs,
|
||||||
errors::*,
|
errors::Result,
|
||||||
files, logic, networking,
|
files, logic, networking,
|
||||||
nmap::{self, Nmaprun},
|
nmap::{self, Nmaprun},
|
||||||
structs::{Args, ResolvData},
|
structs::ResolvData,
|
||||||
},
|
},
|
||||||
|
hickory_resolver::config::{LookupIpStrategy, ResolverOpts},
|
||||||
log::{error, info},
|
log::{error, info},
|
||||||
prettytable,
|
prettytable,
|
||||||
prettytable::Table,
|
prettytable::Table,
|
||||||
|
|
@ -15,61 +16,55 @@ use {
|
||||||
net::Ipv4Addr,
|
net::Ipv4Addr,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
},
|
},
|
||||||
trust_dns_resolver::{
|
|
||||||
config::{LookupIpStrategy, ResolverOpts},
|
|
||||||
Resolver,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
lazy_static! {
|
fn create_resolvers(args: &ProcessedArgs) -> Vec<Ipv4Addr> {
|
||||||
pub static ref RESOLVERS: Vec<Ipv4Addr> = {
|
|
||||||
let args = args::get_args();
|
|
||||||
let mut resolver_ips = Vec::new();
|
let mut resolver_ips = Vec::new();
|
||||||
if args.custom_resolvers {
|
if args.custom_resolvers {
|
||||||
for r in &files::return_file_targets(&args, args.resolvers.clone()) {
|
for r in &files::return_file_targets(args, args.resolvers.clone()) {
|
||||||
match r.parse::<Ipv4Addr>() {
|
match r.parse::<Ipv4Addr>() {
|
||||||
Ok(ip) => resolver_ips.push(ip),
|
Ok(ip) => resolver_ips.push(ip),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error parsing the {} IP from resolvers file to IP address. Please check and try again. Error: {}\n", r, e);
|
error!("Error parsing the {r} IP from resolvers file to IP address. Please check and try again. Error: {e}\n");
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for r in args.resolvers {
|
for r in &args.resolvers {
|
||||||
match r.parse::<Ipv4Addr>() {
|
match r.parse::<Ipv4Addr>() {
|
||||||
Ok(ip) => resolver_ips.push(ip),
|
Ok(ip) => resolver_ips.push(ip),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error parsing the {} IP from resolvers file to IP address. Please check and try again. Error: {}\n", r, e);
|
error!("Error parsing the {r} IP from resolvers file to IP address. Please check and try again. Error: {e}\n");
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolver_ips
|
resolver_ips
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parallel_resolver_all(args: &mut Args) -> Result<()> {
|
pub fn parallel_resolver_all(args: &mut ProcessedArgs) -> Result<()> {
|
||||||
files::check_full_path(&args.logs_dir);
|
if !files::check_full_path(&args.logs_dir) {
|
||||||
|
error!("The logs directory {} does not exist.\n", args.logs_dir);
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
if !args.quiet_flag {
|
if !args.quiet_flag {
|
||||||
info!(
|
info!(
|
||||||
"Performing parallel resolution for {} targets with {} threads, it will take a while...\n",
|
"Performing parallel resolution for {} targets with {} threads, it will take a while...\n",
|
||||||
args.targets.len(), args.threads
|
args.targets.len(), args.threads
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let opts = ResolverOpts {
|
let mut opts = ResolverOpts::default();
|
||||||
timeout: Duration::from_secs(1),
|
opts.timeout = Duration::from_secs(1);
|
||||||
ip_strategy: LookupIpStrategy::Ipv4Only,
|
opts.ip_strategy = LookupIpStrategy::Ipv4Only;
|
||||||
num_concurrent_reqs: 1,
|
opts.num_concurrent_reqs = 1;
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let resolver = networking::get_resolver(networking::return_socket_address(args), opts);
|
let resolver = networking::get_resolver(networking::return_socket_address(args), opts);
|
||||||
|
|
||||||
let data = parallel_resolver_engine(args, args.targets.clone(), resolver);
|
let data = parallel_resolver_engine(args, &args.targets, &resolver);
|
||||||
|
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
table.set_titles(row![
|
table.set_titles(row![
|
||||||
|
|
@ -79,9 +74,9 @@ pub fn parallel_resolver_all(args: &mut Args) -> Result<()> {
|
||||||
"SERVICES"
|
"SERVICES"
|
||||||
]);
|
]);
|
||||||
if args.raw_output && !args.quiet_flag {
|
if args.raw_output && !args.quiet_flag {
|
||||||
println!("HOST,IP,PORT,SERVICE,VERSION,PRODUCT,OS,EXTRAINFO")
|
println!("HOST,IP,PORT,SERVICE,VERSION,PRODUCT,OS,EXTRAINFO");
|
||||||
} else if args.url_output && !args.quiet_flag {
|
} else if args.url_output && !args.quiet_flag {
|
||||||
println!("HOST:IP")
|
println!("HOST:IP");
|
||||||
}
|
}
|
||||||
for (target, resolv_data) in &data {
|
for (target, resolv_data) in &data {
|
||||||
if !resolv_data.ip.is_empty() {
|
if !resolv_data.ip.is_empty() {
|
||||||
|
|
@ -120,11 +115,11 @@ pub fn parallel_resolver_all(args: &mut Args) -> Result<()> {
|
||||||
.extrainfo
|
.extrainfo
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| "NULL".to_string())
|
.unwrap_or_else(|| "NULL".to_string())
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
} else if args.url_output {
|
} else if args.url_output {
|
||||||
for port_data in &resolv_data.ports_data {
|
for port_data in &resolv_data.ports_data {
|
||||||
println!("{}:{}", target, port_data.portid)
|
println!("{}:{}", target, port_data.portid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut services_table = Table::new();
|
let mut services_table = Table::new();
|
||||||
|
|
@ -179,7 +174,7 @@ pub fn parallel_resolver_all(args: &mut Args) -> Result<()> {
|
||||||
error!(
|
error!(
|
||||||
"An error occurred while writing the output file {}.\n",
|
"An error occurred while writing the output file {}.\n",
|
||||||
args.file_name
|
args.file_name
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
if !args.quiet_flag && !args.raw_output && !args.url_output {
|
if !args.quiet_flag && !args.raw_output && !args.url_output {
|
||||||
table.printstd();
|
table.printstd();
|
||||||
|
|
@ -199,36 +194,51 @@ pub fn parallel_resolver_all(args: &mut Args) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parallel_resolver_engine(
|
fn parallel_resolver_engine(
|
||||||
args: &Args,
|
args: &ProcessedArgs,
|
||||||
targets: HashSet<String>,
|
targets: &HashSet<String>,
|
||||||
resolver: Resolver,
|
resolver: &hickory_resolver::TokioResolver,
|
||||||
) -> HashMap<String, ResolvData> {
|
) -> HashMap<String, ResolvData> {
|
||||||
let resolv_data: HashMap<String, ResolvData> = targets
|
let resolv_data: HashMap<String, ResolvData> = targets
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|target| {
|
.map(|target| {
|
||||||
let fqdn_target = format!("{}.", target);
|
let fqdn_target = format!("{target}.");
|
||||||
let mut resolv_data = ResolvData::default();
|
let mut resolv_data = ResolvData::default();
|
||||||
resolv_data.ip = networking::get_records(&resolver, &fqdn_target);
|
resolv_data.ip = networking::get_records(resolver, &fqdn_target);
|
||||||
(target.to_owned(), resolv_data)
|
(target.to_owned(), resolv_data)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let resolvers = create_resolvers(args);
|
||||||
|
|
||||||
let mut nmap_ips: HashSet<String> = resolv_data
|
let mut nmap_ips: HashSet<String> = resolv_data
|
||||||
.values()
|
.values()
|
||||||
.map(|resolv_data| resolv_data.ip.clone())
|
.map(|resolv_data| resolv_data.ip.clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let nmap_ips_orig = nmap_ips.clone();
|
||||||
|
|
||||||
nmap_ips.retain(|ip| {
|
nmap_ips.retain(|ip| {
|
||||||
!ip.is_empty()
|
!ip.is_empty()
|
||||||
&& ip.parse::<Ipv4Addr>().is_ok()
|
|
||||||
&& !ip.parse::<Ipv4Addr>().unwrap().is_private()
|
&& !ip.parse::<Ipv4Addr>().unwrap().is_private()
|
||||||
|
&& ip.parse::<Ipv4Addr>().is_ok()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if nmap_ips.is_empty() {
|
||||||
|
error!("No valid IPs found for scanning. IPs found: {nmap_ips_orig:?}\n");
|
||||||
|
std::process::exit(1)
|
||||||
|
} else {
|
||||||
let nmap_data: HashMap<String, Nmaprun> = nmap_ips
|
let nmap_data: HashMap<String, Nmaprun> = nmap_ips
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|ip| {
|
.map(|ip| {
|
||||||
let filename = format!("{}/{}.xml", &args.logs_dir, &ip);
|
let filename = format!("{}/{}.xml", &args.logs_dir, &ip);
|
||||||
match nmap::get_nmap_data(&filename, ip, &args.min_rate, &args.ports, args.fast_scan) {
|
match nmap::get_nmap_data(
|
||||||
|
&filename,
|
||||||
|
ip,
|
||||||
|
&args.min_rate,
|
||||||
|
&args.ports,
|
||||||
|
args.fast_scan,
|
||||||
|
&resolvers,
|
||||||
|
) {
|
||||||
Ok(nmap_data) => {
|
Ok(nmap_data) => {
|
||||||
nmap_data
|
nmap_data
|
||||||
.host
|
.host
|
||||||
|
|
@ -239,7 +249,7 @@ fn parallel_resolver_engine(
|
||||||
.port
|
.port
|
||||||
.retain(|f| f.state.state == "open");
|
.retain(|f| f.state.state == "open");
|
||||||
if args.no_keep_nmap_logs && std::fs::remove_file(&filename).is_err() {
|
if args.no_keep_nmap_logs && std::fs::remove_file(&filename).is_err() {
|
||||||
error!("Error removing filename {}.", &filename)
|
error!("Error removing filename {}.", &filename);
|
||||||
}
|
}
|
||||||
(ip.clone(), nmap_data)
|
(ip.clone(), nmap_data)
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +263,7 @@ fn parallel_resolver_engine(
|
||||||
|
|
||||||
// Delete the args.logs_dir directory if it's empty
|
// Delete the args.logs_dir directory if it's empty
|
||||||
if args.no_keep_nmap_logs && std::fs::remove_dir(&args.logs_dir).is_err() {
|
if args.no_keep_nmap_logs && std::fs::remove_dir(&args.logs_dir).is_err() {
|
||||||
error!("Error removing directory {}.", &args.logs_dir)
|
error!("Error removing directory {}.", &args.logs_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolv_data
|
resolv_data
|
||||||
|
|
@ -282,3 +292,4 @@ fn parallel_resolver_engine(
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,4 @@
|
||||||
use {
|
use crate::nmap::Port;
|
||||||
crate::nmap::Port,
|
|
||||||
std::{collections::HashSet, time::Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Args {
|
|
||||||
pub target: String,
|
|
||||||
pub file_name: String,
|
|
||||||
pub version: String,
|
|
||||||
pub logs_dir: String,
|
|
||||||
pub threads: usize,
|
|
||||||
pub ports: String,
|
|
||||||
pub with_output: bool,
|
|
||||||
pub unique_output_flag: bool,
|
|
||||||
pub min_rate: String,
|
|
||||||
pub from_file_flag: bool,
|
|
||||||
pub quiet_flag: bool,
|
|
||||||
pub custom_resolvers: bool,
|
|
||||||
pub custom_ports_range: bool,
|
|
||||||
pub no_keep_nmap_logs: bool,
|
|
||||||
pub raw_output: bool,
|
|
||||||
pub fast_scan: bool,
|
|
||||||
pub url_output: bool,
|
|
||||||
pub from_stdin: bool,
|
|
||||||
pub files: Vec<String>,
|
|
||||||
pub resolvers: Vec<String>,
|
|
||||||
pub targets: HashSet<String>,
|
|
||||||
pub time_wasted: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct ResolvData {
|
pub struct ResolvData {
|
||||||
|
|
@ -35,8 +6,8 @@ pub struct ResolvData {
|
||||||
pub ports_data: Vec<Port>,
|
pub ports_data: Vec<Port>,
|
||||||
}
|
}
|
||||||
impl ResolvData {
|
impl ResolvData {
|
||||||
pub fn default() -> ResolvData {
|
pub const fn default() -> Self {
|
||||||
ResolvData {
|
Self {
|
||||||
ip: String::new(),
|
ip: String::new(),
|
||||||
ports_data: Vec::new(),
|
ports_data: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
106
unimap.1
106
unimap.1
|
|
@ -1,81 +1,73 @@
|
||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.3.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||||
.TH UNIMAP "1" "May 2021" "Unimap 0.5.1" "User Commands"
|
.TH UNIMAP "1" "August 2025" "unimap 0.7.0" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
Unimap \- manual page for Unimap 0.5.1
|
unimap \- manual page for unimap 0.7.0
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B unimap
|
||||||
|
[\fI\,OPTIONS\/\fR]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
Unimap 0.5.1
|
|
||||||
Eduard Tolosa <edu4rdshl@protonmail.com>
|
|
||||||
Scan only once by IP address and reduce scan times with Nmap for large amounts of data.
|
Scan only once by IP address and reduce scan times with Nmap for large amounts of data.
|
||||||
.SS "USAGE:"
|
.SH OPTIONS
|
||||||
.IP
|
|
||||||
unimap [FLAGS] [OPTIONS]
|
|
||||||
.SS "FLAGS:"
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-fast\-scan\fR
|
\fB\-t\fR, \fB\-\-target\fR <TARGET>
|
||||||
Use fast scanning for ports (no version detection).
|
Target host
|
||||||
.TP
|
.TP
|
||||||
\fB\-h\fR, \fB\-\-help\fR
|
\fB\-f\fR, \fB\-\-files\fR <FILES>
|
||||||
Prints help information
|
Use a list of targets written in a file as input
|
||||||
.TP
|
|
||||||
\fB\-k\fR, \fB\-\-no\-keep\-nmap\-logs\fR
|
|
||||||
Keep Nmap XML files created in the logs/ folder for every scanned IP. This data will be
|
|
||||||
useful for other tasks.
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-o\fR, \fB\-\-output\fR
|
\fB\-o\fR, \fB\-\-output\fR
|
||||||
Write to an output file. The name of the output file will be unimap\-log\-date.
|
Write to an output file. The name of the output file will be unimap\-log\-date
|
||||||
|
.TP
|
||||||
|
\fB\-u\fR, \fB\-\-unique\-output\fR <UNIQUE_OUTPUT>
|
||||||
|
Write the output in CSV format to the specified filename
|
||||||
.TP
|
.TP
|
||||||
\fB\-q\fR, \fB\-\-quiet\fR
|
\fB\-q\fR, \fB\-\-quiet\fR
|
||||||
Remove informative messages.
|
Remove informative messages
|
||||||
|
.TP
|
||||||
|
\fB\-\-threads\fR <THREADS>
|
||||||
|
Number of threads to use to perform the resolution
|
||||||
|
.TP
|
||||||
|
\fB\-\-resolvers\fR <CUSTOM_RESOLVERS>
|
||||||
|
Path to a file (or files) containing a list of DNS IP address. If no specified then a list of built\-in DNS servers is used
|
||||||
|
.TP
|
||||||
|
\fB\-\-ports\fR <PORTS>
|
||||||
|
Ports to scan. You can specify a range of ports, a list, or both. Put them inside double quotes, for example: "22, 80, 443, 1000\-5000"
|
||||||
|
.TP
|
||||||
|
\fB\-\-min\-rate\fR <MIN_RATE>
|
||||||
|
Nmap \fB\-\-min\-rate\fR value for ports scan
|
||||||
|
.TP
|
||||||
|
\fB\-\-fast\-scan\fR
|
||||||
|
Use fast scanning for ports (no version detection)
|
||||||
|
.TP
|
||||||
|
\fB\-\-logs\-dir\fR <LOGS_DIR>
|
||||||
|
Path to save the CSV data of the process and/or Nmap XML files. Default to logs/ [default: unimap_logs]
|
||||||
|
.TP
|
||||||
|
\fB\-k\fR, \fB\-\-no\-keep\-nmap\-logs\fR
|
||||||
|
Keep Nmap XML files created in the logs/ folder for every scanned IP. This data will be useful for other tasks
|
||||||
.TP
|
.TP
|
||||||
\fB\-r\fR, \fB\-\-raw\-output\fR
|
\fB\-r\fR, \fB\-\-raw\-output\fR
|
||||||
Use raw output instead of a table.
|
Use raw output instead of a table
|
||||||
.TP
|
|
||||||
\fB\-\-stdin\fR
|
|
||||||
Read from stdin instead of files or aguments.
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-url\-output\fR
|
\fB\-\-url\-output\fR
|
||||||
Use HOST:IP output format.
|
Use HOST:IP output format
|
||||||
|
.TP
|
||||||
|
\fB\-\-stdin\fR
|
||||||
|
Read from stdin instead of files or arguments
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR, \fB\-\-help\fR
|
||||||
|
Print help
|
||||||
.TP
|
.TP
|
||||||
\fB\-V\fR, \fB\-\-version\fR
|
\fB\-V\fR, \fB\-\-version\fR
|
||||||
Prints version information
|
Print version
|
||||||
.SS "OPTIONS:"
|
|
||||||
.TP
|
|
||||||
\fB\-\-resolvers\fR <custom\-resolvers>...
|
|
||||||
Path to a file (or files) containing a list of DNS IP address. If no
|
|
||||||
specified then 1.6k of built\-in valid DNS servers from public\-dns.info are
|
|
||||||
used.
|
|
||||||
.TP
|
|
||||||
\fB\-f\fR, \fB\-\-file\fR <files>...
|
|
||||||
Use a list of targets writen in a file as input.
|
|
||||||
.TP
|
|
||||||
\fB\-\-logs\-dir\fR <logs\-dir>
|
|
||||||
Path to save the CSV data of the process and/or Nmap XML files. Default to
|
|
||||||
logs/.
|
|
||||||
.TP
|
|
||||||
\fB\-\-min\-rate\fR <min\-rate>
|
|
||||||
Nmap \fB\-\-min\-rate\fR value for ports scan.
|
|
||||||
.TP
|
|
||||||
\fB\-\-ports\fR <ports>
|
|
||||||
Ports to scan. You can specify a range of ports, a list, or both. Put them
|
|
||||||
inside double quotes, for example: "22, 80, 443, 1000\-5000"
|
|
||||||
.TP
|
|
||||||
\fB\-t\fR, \fB\-\-target\fR <target>
|
|
||||||
Target host.
|
|
||||||
.TP
|
|
||||||
\fB\-\-threads\fR <threads>
|
|
||||||
Number of threads to use to perform the resolution.
|
|
||||||
.TP
|
|
||||||
\fB\-u\fR, \fB\-\-unique\-output\fR <unique\-output>
|
|
||||||
Write the output in CSV format to the specified filename.
|
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
The full documentation for
|
The full documentation for
|
||||||
.B Unimap
|
.B unimap
|
||||||
is maintained as a Texinfo manual. If the
|
is maintained as a Texinfo manual. If the
|
||||||
.B info
|
.B info
|
||||||
and
|
and
|
||||||
.B Unimap
|
.B unimap
|
||||||
programs are properly installed at your site, the command
|
programs are properly installed at your site, the command
|
||||||
.IP
|
.IP
|
||||||
.B info Unimap
|
.B info unimap
|
||||||
.PP
|
.PP
|
||||||
should give you access to the complete manual.
|
should give you access to the complete manual.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue