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;