use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; fn parse_bsd_part(part: &str) -> Result { 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 { 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 = 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 { // First try to parse as IPv6 if let Ok(ip) = s.parse::() { 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)) }