shark_scan/
parser.rs

1use clap::Parser;
2
3/// The Args struct is used to contain and parse command line arguments
4///
5/// -t --target <target ip address> The IP address to scan
6///
7/// -v --verbosity <[none, low, high]> The level of program output
8///
9/// -n --threads <int> The number of threads to use
10///
11/// -p --port-range <int:int,int,int> The port range or comma separated ports to scan
12///
13/// -m --timeout <int> The number of milliseconds to wait for a connection on a given port
14///
15/// --probe When this is provided an HTTP GET request will be sent to the port
16///
17/// Do not use --probe when scanning untrusted hosts as they may send a malicious response
18#[derive(Parser, Debug)]
19#[command(author, version, about, long_about = None)]
20pub struct Args {
21    /// The target IP address to scan
22    #[arg(short = 't', long)]
23    pub(crate) target: String,
24    /// The verbosity level (none, low, high)
25    #[arg(short, long, default_value = "none")]
26    pub verbosity: String,
27    // The number of threads to use
28    #[arg(short = 'n', long, default_value = "4")]
29    pub(crate) threads: usize,
30    /// The port range to scan in the format start:end or comma separated
31    #[arg(short = 'p', long, default_value = "1:1024")]
32    pub(crate) port_range: String,
33    /// The time in milliseconds to await successful port connection
34    #[arg(short = 'm', long, default_value = "100")]
35    pub(crate) timeout: u64,
36    /// ***Do not use against untrusted hosts***
37    /// Probe the socket by performing an HTTP GET request
38    #[arg(long)]
39    pub(crate) probe: bool,
40}
41
42pub(crate) fn parse_ports(port_arg: &str) -> Vec<u16> {
43    let mut ports = Vec::new();
44    for port in port_arg.split(',') {
45        let port = port.trim();
46        if port.contains(':') {
47            let range: Vec<&str> = port.split(':').collect();
48            if range.len() == 2 {
49                let start: u16 = range[0]
50                    .parse()
51                    .expect("Invalid start port, expected similar to -p 1:1024");
52                let end: u16 = range[1]
53                    .parse()
54                    .expect("Invalid end port, expected similar to -p 1:1024");
55                for port in start..=end {
56                    ports.push(port);
57                }
58            } else {
59                panic!("Invalid port: expected similar to -p 1:1024");
60            }
61        } else {
62            let port: u16 = port.parse().expect(&format!("Invalid port: {}", port));
63            ports.push(port);
64        }
65    }
66
67    ports
68}
69
70#[cfg(test)]
71mod tests {
72    use super::parse_ports;
73    #[test]
74    fn test_parse_ports() {
75        let port_range = "20:25,31,32,45:50";
76        let ports = parse_ports(port_range);
77        assert_eq!(
78            ports,
79            vec![20, 21, 22, 23, 24, 25, 31, 32, 45, 46, 47, 48, 49, 50]
80        );
81    }
82
83    #[test]
84    fn test_parse_ports_list_trimmed() {
85        let port_range = "14, 15, 29";
86        let ports = parse_ports(port_range);
87        assert_eq!(ports, vec![14, 15, 29]);
88    }
89
90    #[test]
91    #[should_panic(expected = "Invalid port:")]
92    fn test_parse_invalid_port_range() {
93        let port_range = "14-15";
94        let _ = parse_ports(port_range);
95    }
96    #[test]
97    #[should_panic(expected = "Invalid port:")]
98    fn test_parse_invalid_port_value() {
99        let port_range = "14, a2";
100        let _ = parse_ports(port_range);
101    }
102}