r_lanlib/targets/
ips.rs

1//! Provides helpers for managing IP target lists
2
3use crate::scanners::ScanError;
4
5use std::{net, str::FromStr, sync::Arc};
6
7#[derive(Debug)]
8/// Represents a list of IP targets
9///
10/// This wrapper is used to cut down on the memory needed to store entire
11/// network IP ranges. Rather than storing all 65536 IPs in a /16 CIDR block, or
12/// a range of IPS, this wrapper allows the storage of just CIDR or range in
13/// string form and then dynamically loops the IPs in that block when needed.
14///
15/// # Panics
16///
17/// Panics if an item in the list is not a valid IP
18///
19/// # Examples
20///
21/// ```
22/// # use std::net;
23/// # use r_lanlib::scanners::ScanError;
24/// # use r_lanlib::targets::ips::IPTargets;
25/// let print_ip = |ip: net::Ipv4Addr| -> Result<(), ScanError> {
26///   println!("ip: {}", ip);
27///   Ok(())
28/// };
29/// let ips = IPTargets::new(
30///     vec![
31///       "192.168.0.1".to_string(),
32///       "172.17.0.1-172.17.0.24".to_string(),
33///       "192.168.68.1/24".to_string(),
34///     ]
35/// );
36/// ips.lazy_loop(print_ip).unwrap();
37/// ```
38pub struct IPTargets(Vec<String>, usize);
39
40fn loop_ips<F: FnMut(net::Ipv4Addr) -> Result<(), ScanError>>(
41    list: &[String],
42    mut cb: F,
43) -> Result<(), ScanError> {
44    for target in list.iter() {
45        if target.contains("-") {
46            // target is range
47            let parts: Vec<&str> = target.split("-").collect();
48
49            let begin = net::Ipv4Addr::from_str(parts[0]).map_err(|e| ScanError {
50                ip: Some(target.to_string()),
51                port: None,
52                error: Box::from(e),
53            })?;
54
55            let end = net::Ipv4Addr::from_str(parts[1]).map_err(|e| ScanError {
56                ip: Some(target.to_string()),
57                port: None,
58                error: Box::from(e),
59            })?;
60
61            let subnet = ipnet::Ipv4Subnets::new(begin, end, 32);
62
63            for ip_net in subnet {
64                for ip in ip_net.hosts() {
65                    cb(ip)?;
66                }
67            }
68        } else if target.contains("/") {
69            // target is cidr block
70            let ip_net = ipnet::Ipv4Net::from_str(target).map_err(|e| ScanError {
71                ip: Some(target.to_string()),
72                port: None,
73                error: Box::from(e),
74            })?;
75
76            for ip in ip_net.hosts() {
77                cb(ip)?;
78            }
79        } else {
80            // target is ip
81            let ip: net::Ipv4Addr = net::Ipv4Addr::from_str(target).map_err(|e| ScanError {
82                ip: Some(target.to_string()),
83                port: None,
84                error: Box::from(e),
85            })?;
86            cb(ip)?;
87        }
88    }
89    Ok(())
90}
91
92impl IPTargets {
93    /// Returns a new instance of IPTargets using the provided list
94    pub fn new(list: Vec<String>) -> Arc<Self> {
95        let mut len = 0;
96
97        loop_ips(&list, |_| {
98            len += 1;
99            Ok(())
100        })
101        .unwrap();
102
103        Arc::new(Self(list, len))
104    }
105
106    /// Returns the true length of the target list. If the underlying
107    /// `Vec<String>` is just `["192.168.0.1/24"]`, then a call to "len" will
108    /// return 256
109    pub fn len(&self) -> usize {
110        self.1
111    }
112
113    /// Returns true if the list is empty
114    pub fn is_empty(&self) -> bool {
115        self.1 == 0
116    }
117
118    /// loops over all targets including those that are not explicitly in the
119    /// list but fall within a range or CIDR block defined in the list
120    pub fn lazy_loop<F: FnMut(net::Ipv4Addr) -> Result<(), ScanError>>(
121        &self,
122        cb: F,
123    ) -> Result<(), ScanError> {
124        loop_ips(&self.0, cb)
125    }
126}
127
128#[cfg(test)]
129#[path = "./ips_tests.rs"]
130mod tests;