aboutsummaryrefslogtreecommitdiff
path: root/src/ip.rs
diff options
context:
space:
mode:
authorache <ache@ache.one>2026-08-04 09:43:58 +0200
committerache <ache@ache.one>2026-08-04 09:43:58 +0200
commitaecb7ac234ddb38772f3a968c3fa43ec3b507315 (patch)
tree8752022ffab608719305103b53209910801f20b4 /src/ip.rs
Init commit
Diffstat (limited to 'src/ip.rs')
-rw-r--r--src/ip.rs91
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))
+}