1use std::net::IpAddr;
2
3pub fn normalize_ip(ip_str: &str) -> String {
5 let cleaned = ip_str.trim();
6 if let Ok(ip) = cleaned.parse::<IpAddr>() {
7 return ip.to_string();
8 }
9 if let Some(pos) = cleaned.rfind(':') {
11 let host = &cleaned[..pos];
12 let host_clean = host.trim_matches(|c| c == '[' || c == ']');
13 if let Ok(ip) = host_clean.parse::<IpAddr>() {
14 return ip.to_string();
15 }
16 }
17 cleaned.to_string()
18}
19
20pub fn ip_matches_cidr(ip_str: &str, cidr: &str) -> bool {
22 let ip = match ip_str.parse::<IpAddr>() {
23 Ok(addr) => addr,
24 Err(_) => return false,
25 };
26
27 let parts: Vec<&str> = cidr.split('/').collect();
28 if parts.is_empty() {
29 return false;
30 }
31
32 let subnet_ip = match parts[0].parse::<IpAddr>() {
33 Ok(addr) => addr,
34 Err(_) => return false,
35 };
36
37 let prefix_len = if parts.len() > 1 {
38 match parts[1].parse::<u8>() {
39 Ok(len) => len,
40 Err(_) => return false,
41 }
42 } else {
43 match ip {
44 IpAddr::V4(_) => 32,
45 IpAddr::V6(_) => 128,
46 }
47 };
48
49 match (ip, subnet_ip) {
50 (IpAddr::V4(ip_v4), IpAddr::V4(sub_v4)) => {
51 if prefix_len > 32 {
52 return false;
53 }
54 let ip_num = u32::from(ip_v4);
55 let sub_num = u32::from(sub_v4);
56 let mask = if prefix_len == 0 {
57 0
58 } else {
59 !0u32 << (32 - prefix_len)
60 };
61 (ip_num & mask) == (sub_num & mask)
62 }
63 (IpAddr::V6(ip_v6), IpAddr::V6(sub_v6)) => {
64 if prefix_len > 128 {
65 return false;
66 }
67 let ip_num = u128::from(ip_v6);
68 let sub_num = u128::from(sub_v6);
69 let mask = if prefix_len == 0 {
70 0
71 } else {
72 !0u128 << (128 - prefix_len)
73 };
74 (ip_num & mask) == (sub_num & mask)
75 }
76 _ => false,
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_normalize_ip() {
86 assert_eq!(normalize_ip("127.0.0.1:8080"), "127.0.0.1");
87 assert_eq!(normalize_ip("[::1]:80"), "::1");
88 assert_eq!(normalize_ip("192.168.1.1"), "192.168.1.1");
89 }
90
91 #[test]
92 fn test_ip_matches_cidr() {
93 assert!(ip_matches_cidr("192.168.1.50", "192.168.1.0/24"));
94 assert!(!ip_matches_cidr("192.168.2.50", "192.168.1.0/24"));
95 assert!(ip_matches_cidr("::1", "::1/128"));
96 }
97}