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<()>>(
41 list: &[String],
42 mut cb: F,
43) -> Result<()> {
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| {
50 RLanLibError::from_net_addr_parse_error(target, e)
51 })?;
52
53 let end = net::Ipv4Addr::from_str(parts[1]).map_err(|e| {
54 RLanLibError::from_net_addr_parse_error(target, e)
55 })?;
56
57 let subnet = ipnet::Ipv4Subnets::new(begin, end, 32);
58
59 for ip_net in subnet {
60 for ip in ip_net.hosts() {
61 cb(ip)?;
62 }
63 }
64 } else if target.contains("/") {
65 // target is cidr block
66 let ip_net = ipnet::Ipv4Net::from_str(target).map_err(|e| {
67 RLanLibError::from_ipnet_addr_parse_error(target, e)
68 })?;
69
70 for ip in ip_net.hosts() {
71 cb(ip)?;
72 }
73 } else {
74 // target is ip
75 let ip: net::Ipv4Addr =
76 net::Ipv4Addr::from_str(target).map_err(|e| {
77 RLanLibError::from_net_addr_parse_error(target, e)
78 })?;
79
80 cb(ip)?;
81 }
82 }
83 Ok(())
84}
85
86impl IPTargets {
87 /// Returns a new instance of IPTargets using the provided list
88 pub fn new(list: Vec<String>) -> Result<Arc<Self>> {
89 let mut len = 0;
90
91 loop_ips(&list, |_| {
92 len += 1;
93 Ok(())
94 })?;
95
96 Ok(Arc::new(Self(list, len)))
97 }
98
99 /// Returns the true length of the target list. If the underlying
100 /// `Vec<String>` is just `["192.168.0.1/24"]`, then a call to "len" will
101 /// return 256
102 pub fn len(&self) -> usize {
103 self.1
104 }
105
106 /// Returns true if the list is empty
107 pub fn is_empty(&self) -> bool {
108 self.1 == 0
109 }
110
111 /// loops over all targets including those that are not explicitly in the
112 /// list but fall within a range or CIDR block defined in the list
113 pub fn lazy_loop<F: FnMut(net::Ipv4Addr) -> Result<()>>(
114 &self,
115 cb: F,
116 ) -> Result<()> {
117 loop_ips(&self.0, cb)
118 }
119}
120
121#[cfg(test)]
122#[path = "./ips_tests.rs"]
123mod tests;