pub fn expand_nmap_style_argv<I>(args: I) -> Vec<String>
where
I: IntoIterator<Item = String>,
{
let args: Vec<String> = args.into_iter().collect();
if args.is_empty() {
return args;
}
let mut out: Vec<String> = Vec::with_capacity(args.len() + 8);
out.push(args[0].clone());
let mut i = 1usize;
while i < args.len() {
let a = &args[i];
if a.as_str() == "-sL" {
out.push("--sL".to_string());
i += 1;
continue;
}
if a.as_str() == "-sn" {
out.push("--sn".to_string());
i += 1;
continue;
}
if a.as_str() == "-sO" {
out.push("--sO".to_string());
i += 1;
continue;
}
if a.as_str() == "-sI" {
out.push("--sI".to_string());
i += 1;
if i < args.len() {
let n = &args[i];
if !n.starts_with('-') {
out.push(n.clone());
i += 1;
}
}
continue;
}
if a.as_str() == "-iL" {
out.push("--iL".to_string());
i += 1;
continue;
}
if a.as_str() == "-iR" {
out.push("--iR".to_string());
i += 1;
continue;
}
if a.len() == 3 && a.starts_with("-o") {
let k = a.as_bytes()[2] as char;
if matches!(k, 'N' | 'X' | 'S' | 'G' | 'A' | 'M' | 'H') {
out.push(format!("--o{k}"));
i += 1;
continue;
}
}
if let Some(rest) = a.strip_prefix('-') {
if rest.is_empty() {
out.push(a.clone());
i += 1;
continue;
}
if rest.starts_with('-') {
out.push(a.clone());
i += 1;
continue;
}
if rest.len() == 2 && rest.starts_with('s') {
let ch = rest.as_bytes()[1] as char;
if ch.is_ascii_alphabetic() {
match ch {
'V' => {
out.push("--version-scan".to_string());
i += 1;
continue;
}
'C' => {
out.push("--script-default".to_string());
i += 1;
continue;
}
_ => {
out.push("--scan-type".to_string());
out.push(ch.to_string());
i += 1;
continue;
}
}
}
}
if rest == "Pn" {
out.push("--no-ping".to_string());
i += 1;
continue;
}
if matches!(rest, "PE" | "PP" | "PM") {
let letter = rest.as_bytes()[1] as char;
out.push(format!("--ping-{letter}"));
i += 1;
continue;
}
if rest == "PO" || rest.starts_with("PO") {
out.push("--ping-ip-proto".to_string());
if rest.len() > 2 {
out.push(rest[2..].to_string());
} else {
out.push("1,2,4".to_string());
}
i += 1;
continue;
}
if rest.len() >= 2 && rest.starts_with('P') {
let kind = rest.as_bytes()[1] as char;
if matches!(kind, 'S' | 'A' | 'U' | 'Y') {
if rest.len() == 2 {
out.push(format!("--ping-{kind}"));
i += 1;
continue;
}
let tail = &rest[2..];
out.push(format!("--ping-{kind}"));
out.push(tail.to_string());
i += 1;
continue;
}
}
if rest.len() == 2 && rest.starts_with('T') {
let d = rest.as_bytes()[1];
if d.is_ascii_digit() && (d - b'0') <= 5 {
out.push("--timing".to_string());
out.push((d - b'0').to_string());
i += 1;
continue;
}
}
if rest.chars().all(|c| c == 'v') && !rest.is_empty() && rest.len() <= 5 {
out.push(format!("--verbosity={}", rest.len()));
i += 1;
continue;
}
if rest.chars().all(|c| c == 'd') && !rest.is_empty() && rest.len() <= 5 {
out.push(format!("--debug={}", rest.len()));
i += 1;
continue;
}
}
out.push(a.clone());
i += 1;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expands_scan_and_timing() {
let v = expand_nmap_style_argv(vec![
"nmaprs".into(),
"-sT".into(),
"-T4".into(),
"host".into(),
]);
assert_eq!(
v,
vec!["nmaprs", "--scan-type", "T", "--timing", "4", "host"]
);
}
#[test]
fn expands_pn_and_ps_ports() {
let v = expand_nmap_style_argv(vec!["nmaprs".into(), "-Pn".into(), "-PS80,443".into()]);
assert_eq!(v, vec!["nmaprs", "--no-ping", "--ping-S", "80,443"]);
}
#[test]
fn expands_so_to_long_flag_not_scan_type_o() {
let v = expand_nmap_style_argv(vec!["nmaprs".into(), "-sO".into(), "127.0.0.1".into()]);
assert_eq!(v, vec!["nmaprs", "--sO", "127.0.0.1"]);
}
#[test]
fn expands_po_default_protos() {
let v = expand_nmap_style_argv(vec!["nmaprs".into(), "-PO".into(), "host".into()]);
assert_eq!(v, vec!["nmaprs", "--ping-ip-proto", "1,2,4", "host"]);
}
#[test]
fn expands_po_with_tail() {
let v = expand_nmap_style_argv(vec!["nmaprs".into(), "-PO6".into()]);
assert_eq!(v, vec!["nmaprs", "--ping-ip-proto", "6"]);
}
#[test]
fn expands_version_and_script_default_short_flags() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sV".into()]),
vec!["nmaprs", "--version-scan"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sC".into()]),
vec!["nmaprs", "--script-default"]
);
}
#[test]
fn expands_sl_sn_il_ir() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sL".into()]),
vec!["nmaprs", "--sL"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sn".into()]),
vec!["nmaprs", "--sn"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-iL".into()]),
vec!["nmaprs", "--iL"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-iR".into()]),
vec!["nmaprs", "--iR"]
);
}
#[test]
fn expands_si_consumes_following_non_flag() {
let v = expand_nmap_style_argv(vec![
"nmaprs".into(),
"-sI".into(),
"192.0.2.1:443".into(),
"10.0.0.1".into(),
]);
assert_eq!(v, vec!["nmaprs", "--sI", "192.0.2.1:443", "10.0.0.1",]);
}
#[test]
fn expands_output_short_flags() {
let v = expand_nmap_style_argv(vec![
"nmaprs".into(),
"-oN".into(),
"out.txt".into(),
"-oA".into(),
"base".into(),
]);
assert_eq!(v, vec!["nmaprs", "--oN", "out.txt", "--oA", "base"]);
}
#[test]
fn expands_verbosity_and_debug_counts() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-vvv".into()]),
vec!["nmaprs", "--verbosity=3"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-dd".into()]),
vec!["nmaprs", "--debug=2"]
);
}
#[test]
fn expands_ping_ack_udp_sctp_tails() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PA443".into()]),
vec!["nmaprs", "--ping-A", "443"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PU53".into()]),
vec!["nmaprs", "--ping-U", "53"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PY38412".into()]),
vec!["nmaprs", "--ping-Y", "38412"]
);
}
#[test]
fn passes_through_double_dash_long_options() {
let v = expand_nmap_style_argv(vec![
"nmaprs".into(),
"--no-ping".into(),
"-p".into(),
"22".into(),
"127.0.0.1".into(),
]);
assert_eq!(v, vec!["nmaprs", "--no-ping", "-p", "22", "127.0.0.1"]);
}
#[test]
fn expand_only_binary_is_unchanged() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into()]),
vec!["nmaprs"]
);
}
#[test]
fn expand_empty_argv_stays_empty() {
assert!(expand_nmap_style_argv(Vec::<String>::new()).is_empty());
}
#[test]
fn expand_pe_pm_icmp_short_flags() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PE".into()]),
vec!["nmaprs", "--ping-E"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PM".into()]),
vec!["nmaprs", "--ping-M"]
);
}
#[test]
fn expand_timing_t0_through_t5() {
for n in 0u8..=5 {
let v = expand_nmap_style_argv(vec!["nmaprs".into(), format!("-T{n}")]);
assert_eq!(
v,
vec!["nmaprs".to_string(), "--timing".to_string(), n.to_string(),]
);
}
}
#[test]
fn expands_syn_ack_window_and_maimon_scan_types() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sS".into()]),
vec!["nmaprs", "--scan-type", "S"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sA".into()]),
vec!["nmaprs", "--scan-type", "A"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sW".into()]),
vec!["nmaprs", "--scan-type", "W"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sM".into()]),
vec!["nmaprs", "--scan-type", "M"]
);
}
#[test]
fn expands_udp_and_connect_scan_types() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sU".into()]),
vec!["nmaprs", "--scan-type", "U"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sT".into()]),
vec!["nmaprs", "--scan-type", "T"]
);
}
#[test]
fn expands_pp_and_pm_icmp_ping_flags() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PP".into()]),
vec!["nmaprs", "--ping-P"]
);
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PM".into()]),
vec!["nmaprs", "--ping-M"]
);
}
#[test]
#[allow(non_snake_case)]
fn expands_oX_and_oG_output_flags() {
let v = expand_nmap_style_argv(vec![
"nmaprs".into(),
"-oX".into(),
"scan.xml".into(),
"-oG".into(),
"scan.gnmap".into(),
]);
assert_eq!(v, vec!["nmaprs", "--oX", "scan.xml", "--oG", "scan.gnmap"]);
}
#[test]
fn expands_ping_ack_without_port_list() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PA".into()]),
vec!["nmaprs", "--ping-A"]
);
}
#[test]
fn expands_ping_udp_default_flag_only() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PU".into()]),
vec!["nmaprs", "--ping-U"]
);
}
#[test]
fn mixed_short_and_long_flags_preserve_order() {
let v = expand_nmap_style_argv(vec![
"nmaprs".into(),
"-Pn".into(),
"--top-ports".into(),
"10".into(),
"-sT".into(),
"127.0.0.1".into(),
]);
assert_eq!(
v,
vec![
"nmaprs",
"--no-ping",
"--top-ports",
"10",
"--scan-type",
"T",
"127.0.0.1",
]
);
}
#[test]
fn single_dash_passes_through_unknown_cluster() {
let v = expand_nmap_style_argv(vec!["nmaprs".into(), "-p".into(), "80".into()]);
assert_eq!(v, vec!["nmaprs", "-p", "80"]);
}
#[test]
fn expand_vvvv_caps_at_five() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-vvvvv".into()]),
vec!["nmaprs", "--verbosity=5"]
);
}
#[test]
fn expands_b_ftp_bounce_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-b".into(), "u:p@h:21".into()]),
vec!["nmaprs", "-b", "u:p@h:21"]
);
}
#[test]
fn expands_fragment_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-f".into()]),
vec!["nmaprs", "-f"]
);
}
#[test]
fn expands_n_no_dns() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-n".into()]),
vec!["nmaprs", "-n"]
);
}
#[test]
fn expands_r_resolve_all() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-R".into()]),
vec!["nmaprs", "-R"]
);
}
#[test]
fn expands_6_ipv6_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-6".into()]),
vec!["nmaprs", "-6"]
);
}
#[test]
fn expands_uppercase_scan_type_via_s_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sU".into()]),
vec!["nmaprs", "--scan-type", "U"]
);
}
#[test]
fn expands_traceroute_long_flag_passthrough() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "--traceroute".into()]),
vec!["nmaprs", "--traceroute"]
);
}
#[test]
fn expands_no_ping_long_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "--no-ping".into()]),
vec!["nmaprs", "--no-ping"]
);
}
#[test]
fn expands_version_scan_short() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sV".into()]),
vec!["nmaprs", "--version-scan"]
);
}
#[test]
fn expands_aggressive_uppercase_a_not_scan_type() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-A".into()]),
vec!["nmaprs", "-A"]
);
}
#[test]
fn expands_syn_scan_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sS".into()]),
vec!["nmaprs", "--scan-type", "S"]
);
}
#[test]
fn expands_connect_scan_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sT".into()]),
vec!["nmaprs", "--scan-type", "T"]
);
}
#[test]
#[allow(non_snake_case)]
fn expands_oN_output_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-oN".into(), "out".into()]),
vec!["nmaprs", "--oN", "out"]
);
}
#[test]
fn expands_privileged_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "--privileged".into()]),
vec!["nmaprs", "--privileged"]
);
}
#[test]
fn expands_multiple_scan_type_flags_sequentially() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sS".into(), "-sU".into()]),
vec!["nmaprs", "--scan-type", "S", "--scan-type", "U"]
);
}
#[test]
#[allow(non_snake_case)]
fn expands_oX_grepable_output_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-oX".into(), "out.xml".into()]),
vec!["nmaprs", "--oX", "out.xml"]
);
}
#[test]
#[allow(non_snake_case)]
fn expands_oG_normal_grepable_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-oG".into(), "out.gnmap".into()]),
vec!["nmaprs", "--oG", "out.gnmap"]
);
}
#[test]
fn expands_ping_echo_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PE".into()]),
vec!["nmaprs", "--ping-E"]
);
}
#[test]
fn expands_unprivileged_long_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "--unprivileged".into()]),
vec!["nmaprs", "--unprivileged"]
);
}
#[test]
fn expands_top_ports_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-F".into()]),
vec!["nmaprs", "-F"]
);
}
#[test]
fn expands_null_scan_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sN".into()]),
vec!["nmaprs", "--scan-type", "N"]
);
}
#[test]
fn expands_fin_scan_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sF".into()]),
vec!["nmaprs", "--scan-type", "F"]
);
}
#[test]
fn expands_xmas_scan_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sX".into()]),
vec!["nmaprs", "--scan-type", "X"]
);
}
#[test]
fn expands_ping_ack_with_port_list() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PA443".into()]),
vec!["nmaprs", "--ping-A", "443"]
);
}
#[test]
fn expands_ping_sctp_init_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PY".into()]),
vec!["nmaprs", "--ping-Y"]
);
}
#[test]
#[allow(non_snake_case)]
fn expands_oM_machine_output_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-oM".into(), "out".into()]),
vec!["nmaprs", "--oM", "out"]
);
}
#[test]
fn expands_timing_t0() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-T0".into()]),
vec!["nmaprs", "--timing", "0"]
);
}
#[test]
fn expands_triple_verbosity() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-vvv".into()]),
vec!["nmaprs", "--verbosity=3"]
);
}
#[test]
fn expands_double_debug() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-dd".into()]),
vec!["nmaprs", "--debug=2"]
);
}
#[test]
fn expands_list_scan_sl_long() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sL".into()]),
vec!["nmaprs", "--sL"]
);
}
#[test]
fn expands_ip_protocol_scan_so() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sO".into()]),
vec!["nmaprs", "--sO"]
);
}
#[test]
fn expands_ping_scan_sn() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sn".into()]),
vec!["nmaprs", "--sn"]
);
}
#[test]
fn expands_maimon_scan_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sM".into()]),
vec!["nmaprs", "--scan-type", "M"]
);
}
#[test]
fn expands_window_scan_short_flag() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sW".into()]),
vec!["nmaprs", "--scan-type", "W"]
);
}
#[test]
fn expands_idle_scan_si_with_zombie() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sI".into(), "10.0.0.1".into()]),
vec!["nmaprs", "--sI", "10.0.0.1"]
);
}
#[test]
fn expands_ftp_bounce_short_b() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-b".into(), "user@127.0.0.1".into()]),
vec!["nmaprs", "-b", "user@127.0.0.1"]
);
}
#[test]
fn expands_ping_syn_with_port_list() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PS22,80".into()]),
vec!["nmaprs", "--ping-S", "22,80"]
);
}
#[test]
fn expands_ping_timestamp_pp() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PP".into()]),
vec!["nmaprs", "--ping-P"]
);
}
#[test]
fn expands_ping_mask_pm() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PM".into()]),
vec!["nmaprs", "--ping-M"]
);
}
#[test]
fn expands_list_scan_sl() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sL".into()]),
vec!["nmaprs", "--sL"]
);
}
#[test]
fn expands_sctp_init_sy() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sY".into()]),
vec!["nmaprs", "--scan-type", "Y"]
);
}
#[test]
fn expands_sctp_cookie_sz() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-sZ".into()]),
vec!["nmaprs", "--scan-type", "Z"]
);
}
#[test]
fn expands_ping_ack_pa_with_ports() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PA443".into()]),
vec!["nmaprs", "--ping-A", "443"]
);
}
#[test]
fn expands_ping_udp_pu() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PU".into()]),
vec!["nmaprs", "--ping-U"]
);
}
#[test]
fn expands_ping_echo_pe() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-PE".into()]),
vec!["nmaprs", "--ping-E"]
);
}
#[test]
fn expands_fast_scan_f() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-F".into()]),
vec!["nmaprs", "-F"]
);
}
#[test]
fn expands_no_dns_n() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-n".into()]),
vec!["nmaprs", "-n"]
);
}
#[test]
fn expands_resolve_r() {
assert_eq!(
expand_nmap_style_argv(vec!["nmaprs".into(), "-R".into()]),
vec!["nmaprs", "-R"]
);
}
}