wgctrl-sys: correct InterfaceName parsing and simplify a bit

It was dropping the last character in the name, and I modified it to
instead just error on any &str that has a '\0' in it. The strictness
feels acceptable and simplifies the code a bit.
pull/37/head
Jake McGinty 2021-04-09 12:27:49 +09:00
parent b1e1ff8f4f
commit bcd68df772
1 changed files with 16 additions and 24 deletions

View File

@ -123,23 +123,19 @@ impl FromStr for InterfaceName {
/// Extra validation logic ported from [iproute2](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/utils.c#n827) /// Extra validation logic ported from [iproute2](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/utils.c#n827)
fn from_str(name: &str) -> Result<Self, InvalidInterfaceName> { fn from_str(name: &str) -> Result<Self, InvalidInterfaceName> {
let len = name.len(); let len = name.len();
// Ensure its short enough to include a trailing NUL if len == 0 {
if len > (libc::IFNAMSIZ - 1) { return Err(InvalidInterfaceName::Empty);
return Err(InvalidInterfaceName::TooLong(len));
} }
if len == 0 || name.trim_start_matches('\0').is_empty() { // Ensure its short enough to include a trailing NUL
return Err(InvalidInterfaceName::Empty); if len > (libc::IFNAMSIZ - 1) {
return Err(InvalidInterfaceName::TooLong);
} }
let mut buf = [c_char::default(); libc::IFNAMSIZ]; let mut buf = [c_char::default(); libc::IFNAMSIZ];
// Check for interior NULs and other invalid characters. // Check for interior NULs and other invalid characters.
for (out, b) in buf.iter_mut().zip(name.as_bytes()[..(len - 1)].iter()) { for (out, b) in buf.iter_mut().zip(name.as_bytes().iter()) {
if *b == 0 { if *b == 0 || *b == b'/' || b.is_ascii_whitespace() {
return Err(InvalidInterfaceName::InteriorNul);
}
if *b == b'/' || b.is_ascii_whitespace() {
return Err(InvalidInterfaceName::InvalidChars); return Err(InvalidInterfaceName::InvalidChars);
} }
@ -197,28 +193,24 @@ impl fmt::Display for InterfaceName {
/// An interface name was bad. /// An interface name was bad.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum InvalidInterfaceName { pub enum InvalidInterfaceName {
/// Provided name had an interior NUL byte.
InteriorNul,
/// Provided name was longer then the interface name length limit /// Provided name was longer then the interface name length limit
/// of the system. /// of the system.
TooLong(usize), TooLong,
// These checks are done in the kernel as well, but no reason to let bad names // These checks are done in the kernel as well, but no reason to let bad names
// get that far: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/utils.c?id=1f420318bda3cc62156e89e1b56d60cc744b48ad#n827. // get that far: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/utils.c?id=1f420318bda3cc62156e89e1b56d60cc744b48ad#n827.
/// Interface name was an empty string. /// Interface name was an empty string.
Empty, Empty,
/// Interface name contained a `/` or space character. /// Interface name contained a nul, `/` or whitespace character.
InvalidChars, InvalidChars,
} }
impl fmt::Display for InvalidInterfaceName { impl fmt::Display for InvalidInterfaceName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::InteriorNul => f.write_str("interface name contained an interior NUL byte"), Self::TooLong => write!(
Self::TooLong(size) => write!(
f, f,
"interface name was {} bytes long but the system's max is {}", "interface name longer than system max of {} chars",
size,
libc::IFNAMSIZ libc::IFNAMSIZ
), ),
Self::Empty => f.write_str("an empty interface name was provided"), Self::Empty => f.write_str("an empty interface name was provided"),
@ -317,17 +309,17 @@ mod tests {
#[test] #[test]
fn test_interface_names() { fn test_interface_names() {
assert!("wg-01".parse::<InterfaceName>().is_ok()); assert_eq!("wg-01".parse::<InterfaceName>().unwrap().as_str_lossy(), "wg-01");
assert!("longer-nul\0".parse::<InterfaceName>().is_ok()); assert!("longer-nul\0".parse::<InterfaceName>().is_err());
let invalid_names = &[ let invalid_names = &[
("", InvalidInterfaceName::Empty), // Empty Rust string ("", InvalidInterfaceName::Empty), // Empty Rust string
("\0", InvalidInterfaceName::Empty), // Empty C string ("\0", InvalidInterfaceName::InvalidChars), // Empty C string
("ifname\0nul", InvalidInterfaceName::InteriorNul), // Contains interior NUL ("ifname\0nul", InvalidInterfaceName::InvalidChars), // Contains interior NUL
("if name", InvalidInterfaceName::InvalidChars), // Contains a space ("if name", InvalidInterfaceName::InvalidChars), // Contains a space
("ifna/me", InvalidInterfaceName::InvalidChars), // Contains a slash ("ifna/me", InvalidInterfaceName::InvalidChars), // Contains a slash
("if na/me", InvalidInterfaceName::InvalidChars), // Contains a space and slash ("if na/me", InvalidInterfaceName::InvalidChars), // Contains a space and slash
("interfacelongname", InvalidInterfaceName::TooLong(17)), // Too long ("interfacelongname", InvalidInterfaceName::TooLong), // Too long
]; ];
for (name, expected) in invalid_names { for (name, expected) in invalid_names {