r_lanlib/targets/
ips.rs

1//! Provides helpers for managing IP target lists
2
3use std::{net, str::FromStr, sync::Arc};
4
5use crate::error::{RLanLibError, Result};
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/// # Errors
16///
17/// Returns an error if an item in the list is not a valid IP, CIDR block, or range
18///
19/// # Examples
20///
21/// ```
22/// # use std::net;
23/// # use r_lanlib::error::Result;
24/// # use r_lanlib::targets::ips::IPTargets;
25/// let print_ip = |ip: net::Ipv4Addr| -> Result<()> {
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/// ).unwrap();
36/// ips.lazy_loop(print_ip).unwrap();
37/// ```
38pub struct IPTargets(Vec<String>, usize);
39
40fn loop_ips<F: FnMut(net::Ipv4Addr) -> Result<()>>(list: &[String], mut cb: F) -> Result<()> {
41    for target in list.iter() {
42        if target.contains("-") {
43            // target is range
44            let parts: Vec<&str> = target.split("-").collect();
45
46            let begin = net::Ipv4Addr::from_str(parts[0])
47                .map_err(|e| RLanLibError::from_net_addr_parse_error(target, e))?;
48
49            let end = net::Ipv4Addr::from_str(parts[1])
50                .map_err(|e| RLanLibError::from_net_addr_parse_error(target, e))?;
51
52            let subnet = ipnet::Ipv4Subnets::new(begin, end, 32);
53
54            for ip_net in subnet {
55                for ip in ip_net.hosts() {
56                    cb(ip)?;
57                }
58            }
59        } else if target.contains("/") {
60            // target is cidr block
61            let ip_net = ipnet::Ipv4Net::from_str(target)
62                .map_err(|e| RLanLibError::from_ipnet_addr_parse_error(target, e))?;
63
64            for ip in ip_net.hosts() {
65                cb(ip)?;
66            }
67        } else {
68            // target is ip
69            let ip: net::Ipv4Addr = net::Ipv4Addr::from_str(target)
70                .map_err(|e| RLanLibError::from_net_addr_parse_error(target, e))?;
71
72            cb(ip)?;
73        }
74    }
75    Ok(())
76}
77
78impl IPTargets {
79    /// Returns a new instance of IPTargets using the provided list
80    pub fn new(list: Vec<String>) -> Result<Arc<Self>> {
81        let mut len = 0;
82
83        loop_ips(&list, |_| {
84            len += 1;
85            Ok(())
86        })?;
87
88        Ok(Arc::new(Self(list, len)))
89    }
90
91    /// Returns the true length of the target list. If the underlying
92    /// `Vec<String>` is just `["192.168.0.1/24"]`, then a call to "len" will
93    /// return 256
94    pub fn len(&self) -> usize {
95        self.1
96    }
97
98    /// Returns true if the list is empty
99    pub fn is_empty(&self) -> bool {
100        self.1 == 0
101    }
102
103    /// loops over all targets including those that are not explicitly in the
104    /// list but fall within a range or CIDR block defined in the list
105    pub fn lazy_loop<F: FnMut(net::Ipv4Addr) -> Result<()>>(&self, cb: F) -> Result<()> {
106        loop_ips(&self.0, cb)
107    }
108}
109
110#[cfg(test)]
111#[path = "./ips_tests.rs"]
112mod tests;