From 235399c7e7fccd19df322ff3d6ec3a682b1e2ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Fri, 12 Feb 2016 18:29:13 +0100 Subject: [PATCH 1/2] Add from_cidr to parse strs --- src/lib.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b685a35..1bd682b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate ip; use std::fmt; use std::net::{Ipv4Addr, Ipv6Addr}; +use std::str::FromStr; use ip::IpAddr; @@ -36,6 +37,13 @@ impl Ipv4Network { } } + pub fn from_cidr(cidr: &str) -> Result { + let (addr_str, prefix_str) = try!(cidr_parts(cidr)); + let addr = try!(Self::parse_addr(addr_str)); + let prefix = try!(parse_prefix(prefix_str, 32)); + Ok(Self::new(addr, prefix)) + } + pub fn ip(&self) -> Ipv4Addr { self.addr } @@ -67,6 +75,20 @@ impl Ipv4Network { let (_, net) = self.network(); (u32::from(ip) & net) == net } + + fn parse_addr(addr: &str) -> Result { + let byte_strs = addr.split('.') + .map(|b| b.parse::()) + .map(|b| b.map_err(|_| format!("Invalid IPv4: {}", addr))); + let mut bytes = [0; 4]; + for (i, byte) in byte_strs.enumerate() { + if i >= 4 { + return Err(format!("Malformed IP: {}", addr)); + } + bytes[i] = try!(byte); + } + Ok(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3])) + } } impl Ipv6Network { @@ -77,6 +99,13 @@ impl Ipv6Network { } } + pub fn from_cidr(cidr: &str) -> Result { + let (addr_str, prefix_str) = try!(cidr_parts(cidr)); + let addr = try!(Self::parse_addr(addr_str)); + let prefix = try!(parse_prefix(prefix_str, 128)); + Ok(Self::new(addr, prefix)) + } + pub fn ip(&self) -> Ipv6Addr { self.addr } @@ -84,6 +113,10 @@ impl Ipv6Network { pub fn prefix(&self) -> u8 { self.prefix } + + fn parse_addr(addr: &str) -> Result { + Ipv6Addr::from_str(addr).map_err(|e| format!("{}", e)) + } } impl IpNetwork { @@ -121,6 +154,25 @@ impl fmt::Debug for Ipv6Network { } } +fn cidr_parts<'a>(cidr: &'a str) -> Result<(&'a str, &'a str), String> { + let parts = cidr.split('/').collect::>(); + if parts.len() == 2 { + Ok((parts[0], parts[1])) + } else { + Err(format!("Malformed cidr: {}", cidr)) + } +} + +fn parse_prefix(prefix: &str, max: u8) -> Result { + let mask = try!(prefix.parse::().map_err(|_| format!("Prefix is NaN"))); + if mask > max { + Err(format!("Prefix must be <= {}", max)) + } else { + Ok(mask) + } +} + + #[cfg(test)] mod test { use std::net::{Ipv4Addr, Ipv6Addr}; @@ -132,12 +184,76 @@ mod test { assert_eq!(cidr.prefix(), 24); } + #[test] + fn parse_v4() { + let cidr = Ipv4Network::from_cidr("0/0").unwrap(); + assert_eq!(cidr.ip(), Ipv4Addr::new(0, 0, 0, 0)); + assert_eq!(cidr.prefix(), 0); + } + + #[test] + fn parse_v4_2() { + let cidr = Ipv4Network::from_cidr("127.1.0.0/24").unwrap(); + assert_eq!(cidr.ip(), Ipv4Addr::new(127, 1, 0, 0)); + assert_eq!(cidr.prefix(), 24); + } + + #[test] + fn parse_v4_fail_addr() { + let cidr = Ipv4Network::from_cidr("10.a.b/8"); + assert!(cidr.is_err()); + } + + #[test] + fn parse_v4_fail_addr2() { + let cidr = Ipv4Network::from_cidr("10.1.1.1.0/8"); + assert!(cidr.is_err()); + } + + #[test] + fn parse_v4_fail_addr3() { + let cidr = Ipv4Network::from_cidr("256/8"); + assert!(cidr.is_err()); + } + + #[test] + fn parse_v4_fail_prefix() { + let cidr = Ipv4Network::from_cidr("0/39"); + assert!(cidr.is_err()); + } + #[test] fn create_v6() { let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24); assert_eq!(cidr.prefix(), 24); } + #[test] + fn parse_v6() { + let cidr = Ipv6Network::from_cidr("::1/0").unwrap(); + assert_eq!(cidr.ip(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + assert_eq!(cidr.prefix(), 0); + } + + #[test] + fn parse_v6_2() { + let cidr = Ipv6Network::from_cidr("FF01:0:0:17:0:0:0:2/64").unwrap(); + assert_eq!(cidr.ip(), Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2)); + assert_eq!(cidr.prefix(), 64); + } + + #[test] + fn parse_v6_fail_addr() { + let cidr = Ipv6Network::from_cidr("2001::1::/8"); + assert!(cidr.is_err()); + } + + #[test] + fn parse_v6_fail_prefix() { + let cidr = Ipv6Network::from_cidr("::1/129"); + assert!(cidr.is_err()); + } + #[test] fn mask_v4() { let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 29); From af76578eace9ab1fb9ef17d90385a385098fb5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Fri, 12 Feb 2016 19:30:56 +0100 Subject: [PATCH 2/2] Add check that IP in CIDR is network --- src/lib.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1bd682b..cd2776b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,9 @@ use std::str::FromStr; use ip::IpAddr; +const IPV4_BITS: u8 = 32; +const IPV6_BITS: u8 = 128; + // A network #[derive(Debug)] pub enum IpNetwork { @@ -40,8 +43,14 @@ impl Ipv4Network { pub fn from_cidr(cidr: &str) -> Result { let (addr_str, prefix_str) = try!(cidr_parts(cidr)); let addr = try!(Self::parse_addr(addr_str)); - let prefix = try!(parse_prefix(prefix_str, 32)); - Ok(Self::new(addr, prefix)) + let prefix = try!(parse_prefix(prefix_str, IPV4_BITS)); + let new = Self::new(addr, prefix); + let (net, _) = new.network(); + if addr != net { + Err(format!("IP must have zeroes in host part")) + } else { + Ok(new) + } } pub fn ip(&self) -> Ipv4Addr { @@ -102,7 +111,7 @@ impl Ipv6Network { pub fn from_cidr(cidr: &str) -> Result { let (addr_str, prefix_str) = try!(cidr_parts(cidr)); let addr = try!(Self::parse_addr(addr_str)); - let prefix = try!(parse_prefix(prefix_str, 128)); + let prefix = try!(parse_prefix(prefix_str, IPV6_BITS)); Ok(Self::new(addr, prefix)) }