diff options
| author | ache <ache@ache.one> | 2026-08-04 09:43:58 +0200 |
|---|---|---|
| committer | ache <ache@ache.one> | 2026-08-04 09:43:58 +0200 |
| commit | aecb7ac234ddb38772f3a968c3fa43ec3b507315 (patch) | |
| tree | 8752022ffab608719305103b53209910801f20b4 /src/ip.rs | |
Init commit
Diffstat (limited to 'src/ip.rs')
| -rw-r--r-- | src/ip.rs | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/src/ip.rs b/src/ip.rs new file mode 100644 index 0000000..068d38b --- /dev/null +++ b/src/ip.rs @@ -0,0 +1,91 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +fn parse_bsd_part(part: &str) -> Result<u32, String> { + let part = part.trim(); + if part.is_empty() { + return Err("empty part".into()); + } + + // Determine base and skip prefix if hex or octal + let (base, start_idx) = if part.starts_with("0x") || part.starts_with("0X") { + (16, 2) + } else if part.starts_with('0') && part.len() > 1 { + (8, 1) + } else { + (10, 0) + }; + + let num_str = &part[start_idx..]; + + // If base is 8 or 16, num_str must not be empty (e.g., "0x" or "0o" is invalid) + if (base == 8 || base == 16) && num_str.is_empty() { + return Err("invalid number format".into()); + } + + u32::from_str_radix(num_str, base).map_err(|e| format!("invalid number: {}", e)) +} + +pub fn parse_bsd_ipv4(s: &str) -> Result<Ipv4Addr, String> { + let parts: Vec<&str> = s.split('.').collect(); + let n = parts.len(); + + if n < 1 || n > 4 { + return Err("invalid number of parts".into()); + } + + let mut parsed_parts: Vec<u32> = Vec::with_capacity(n); + for part in parts { + let parsed = parse_bsd_part(part)?; + parsed_parts.push(parsed); + } + + let addr: u32 = match n { + 4 => { + if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFF || + parsed_parts[2] > 0xFF || parsed_parts[3] > 0xFF { + return Err("invalid IP part out of range".into()); + } + (parsed_parts[0] << 24) | + (parsed_parts[1] << 16) | + (parsed_parts[2] << 8) | + parsed_parts[3] + } + 3 => { + if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFF || parsed_parts[2] > 0xFFFF { + return Err("invalid IP part out of range".into()); + } + (parsed_parts[0] << 24) | + (parsed_parts[1] << 16) | + parsed_parts[2] + } + 2 => { + if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFFFFFF { + return Err("invalid IP part out of range".into()); + } + (parsed_parts[0] << 24) | + parsed_parts[1] + } + 1 => { + parsed_parts[0] + } + _ => { + return Err("invalid number of parts".into()); + } + }; + + Ok(Ipv4Addr::from(addr)) +} + +pub fn parse_ip(s: &str) -> Result<IpAddr, String> { + // First try to parse as IPv6 + if let Ok(ip) = s.parse::<Ipv6Addr>() { + return Ok(IpAddr::V6(ip)); + } + + // Try to parse as BSD valid IPv4 + if let Ok(ipv4) = parse_bsd_ipv4(s) { + return Ok(IpAddr::V4(ipv4)); + } + + Err(format!("invalid IP address: {}", s)) +} |