wrote the tool
This commit is contained in:
1 parent
541584efa4
commit
a3e1d5fdf0
3 files changed
+151
-1
No files matched your search
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "scope_filter"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.6.7", features = ["derive"] }
|
||||
ipnet = "2.12.2"
|
||||
@@ -1,3 +1,6 @@
|
||||
# scope_filter
|
||||
|
||||
Takes your scope and exclusion list, then removes the excluded IPs and outputs the remaining IPs.
|
||||
Takes your scope and exclusion list, then removes the excluded IPs and outputs the remaining IPs.
|
||||
|
||||
# Usage:
|
||||
scope_filter -s /path/to/scope/file (formated one ip/range/subnet per line) -e /path/to/exclusions/file (formated one ip/range/subnet per line) -o /path/to/output/file
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
use clap::Parser;
|
||||
use ipnet::{Ipv4AddrRange, Ipv4Net};
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "Subnet Processor",
|
||||
author,
|
||||
version,
|
||||
about = "Removes excluded IPs/subnets/ranges from input subnets, ranges, or IPs and outputs ranges or single IPs."
|
||||
)]
|
||||
struct Args {
|
||||
#[arg(short, long, value_name = "SCOPE_FILE")]
|
||||
scope_file: PathBuf,
|
||||
|
||||
#[arg(short, long, value_name = "EXCLUSION_FILE")]
|
||||
exclusion_file: PathBuf,
|
||||
|
||||
#[arg(short, long, value_name = "OUTPUT_FILE")]
|
||||
output_file: PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let args = Args::parse();
|
||||
let subnet_lines = read_lines_to_vec(&args.scope_file)?;
|
||||
let exclusion_lines = read_lines_to_vec(&args.exclusion_file)?;
|
||||
let mut excluded_ips = HashSet::new();
|
||||
for line in exclusion_lines {
|
||||
match parse_ip_entry(&line) {
|
||||
Ok(ips) => {
|
||||
for ip in ips {
|
||||
excluded_ips.insert(ip);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Invalid exclusion entry '{}': {}", line, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut output_file = File::create(&args.output_file)?;
|
||||
for subnet_str in subnet_lines {
|
||||
let subnet_ips = match parse_ip_entry(&subnet_str) {
|
||||
Ok(ips) => ips,
|
||||
Err(e) => {
|
||||
eprintln!("Invalid input entry '{}': {}", subnet_str, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if subnet_ips.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let remaining_ips: Vec<Ipv4Addr> = subnet_ips
|
||||
.into_iter()
|
||||
.filter(|ip| !excluded_ips.contains(ip))
|
||||
.collect();
|
||||
if remaining_ips.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut ranges: Vec<(Ipv4Addr, Ipv4Addr)> = Vec::new();
|
||||
let mut range_start = remaining_ips[0];
|
||||
let mut range_end = remaining_ips[0];
|
||||
for &ip in remaining_ips.iter().skip(1) {
|
||||
if u32::from(ip) == u32::from(range_end) + 1 {
|
||||
range_end = ip;
|
||||
} else {
|
||||
ranges.push((range_start, range_end));
|
||||
range_start = ip;
|
||||
range_end = ip;
|
||||
}
|
||||
}
|
||||
ranges.push((range_start, range_end));
|
||||
for (start, end) in ranges {
|
||||
if start == end {
|
||||
writeln!(output_file, "{}", start)?;
|
||||
} else {
|
||||
writeln!(output_file, "{}-{}", start, end)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
println!(
|
||||
"Processing complete! Output written to '{}'.",
|
||||
args.output_file.display()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
fn parse_ip_entry(s: &str) -> io::Result<Vec<Ipv4Addr>> {
|
||||
let s = s.trim();
|
||||
if s.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
if let Ok(ip) = Ipv4Addr::from_str(s) {
|
||||
return Ok(vec![ip]);
|
||||
}
|
||||
if let Ok(net) = Ipv4Net::from_str(s) {
|
||||
let range = Ipv4AddrRange::new(net.network(), net.broadcast());
|
||||
return Ok(range.collect());
|
||||
}
|
||||
if let Some((start_str, end_str)) = s.split_once('-') {
|
||||
let start = Ipv4Addr::from_str(start_str.trim())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))?;
|
||||
let end = Ipv4Addr::from_str(end_str.trim())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))?;
|
||||
|
||||
if u32::from(start) > u32::from(end) {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("Invalid range {}: start IP is greater than end IP", s),
|
||||
));
|
||||
}
|
||||
|
||||
let range = Ipv4AddrRange::new(start, end);
|
||||
return Ok(range.collect());
|
||||
}
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("Unrecognized IP format: {}", s),
|
||||
))
|
||||
}
|
||||
fn read_lines_to_vec<P>(filename: P) -> io::Result<Vec<String>>
|
||||
where
|
||||
P: AsRef<std::path::Path>,
|
||||
{
|
||||
let file = File::open(filename)?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut lines = Vec::new();
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
let trimmed = line.trim();
|
||||
if !trimmed.is_empty() {
|
||||
lines.push(trimmed.to_string());
|
||||
}
|
||||
}
|
||||
Ok(lines)
|
||||
}
|
||||
Reference in new issue
Block a user