diff --git a/src/ptools.rs b/src/ptools.rs index 39522de..8b25c86 100644 --- a/src/ptools.rs +++ b/src/ptools.rs @@ -133,7 +133,12 @@ struct ParseError { } impl ParseError { - fn new(file: &str, reason: &str) -> Self { + fn new(item: &str, reason: &str) -> Self { + ParseError { + reason: format!("Error parsing {}: {}", item, reason), + } + } + fn in_file(file: &str, reason: &str) -> Self { ParseError { reason: format!("Error parsing /proc/[pid]/{}: {}", file, reason), } @@ -182,7 +187,7 @@ impl ProcStat { let s: String = s?; let substrs = s.splitn(2, ":").collect::>(); if substrs.len() < 2 { - Err(ParseError::new( + Err(ParseError::in_file( "status", &format!( "Fewer fields than expected in line '{}' of file {}", @@ -208,7 +213,7 @@ impl ProcStat { fn get_field(&self, field: &str) -> Result<&str, Box> { match self.fields.get(field) { Some(val) => Ok(val), - None => Err(From::from(ParseError::new( + None => Err(From::from(ParseError::in_file( "status", &format!( "Missing expected field '{}' file {}", @@ -646,17 +651,27 @@ fn parse_sock_type(type_code: &str) -> SockType { } } -// Parse a socket address of the form "0100007F:1538" (i.e. 127.0.0.1:1538) -fn parse_ipv4_sock_addr(s: &str) -> Result> { - let port = u16::from_str_radix(s.split(':').collect::>()[1], 16).unwrap(); - let addr = u32::from_str_radix(s.split(':').collect::>()[0], 16).unwrap(); - // TODO do we need to change 'addr' to network order? - let addr = Ipv4Addr::new( - ((addr >> 24) & 0xFF) as u8, - ((addr >> 16) & 0xFF) as u8, - ((addr >> 8) & 0xFF) as u8, - (addr & 0xFF) as u8, - ); +// Parse a socket address of the form "0100007F:1538" (i.e. 127.0.0.1:5432) +fn parse_ipv4_sock_addr(s: &str) -> Result { + let mk_err = || { + ParseError::new( + "IPv4 address", + &format!("expected address in form '0100007F:1538', got {}", s), + ) + }; + + let fields = s.split(':').collect::>(); + if fields.len() != 2 { + return Err(mk_err()); + } + + // Port is always printed with most-significant byte first. + let port = u16::from_str_radix(fields[1], 16).map_err(|_| mk_err())?; + + // Address is printed with most-significant byte first on big-endian systems and vice-versa on + // little-endian systems. + let addr_native_endian = u32::from_str_radix(fields[0], 16).map_err(|_| mk_err())?; + let addr = Ipv4Addr::from(addr_native_endian.to_be()); Ok(SocketAddr::new(IpAddr::V4(addr), port)) } @@ -916,3 +931,20 @@ pub fn ptree_main() { } } } + +mod test { + use super::*; + use std::net::SocketAddr; + + #[test] + fn test_parse_ipv4_sock_addr() { + assert_eq!( + parse_ipv4_sock_addr("0100007F:1538").unwrap(), + "127.0.0.1:5432".parse::().unwrap() + ); + + assert!(parse_ipv4_sock_addr("0100007F 1538").is_err()); + assert!(parse_ipv4_sock_addr("010000YY:1538").is_err()); + assert!(parse_ipv4_sock_addr("0100007F:15YY").is_err()); + } +}