diff --git a/Cargo.toml b/Cargo.toml index 25a0f6c..8268ab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,11 @@ repository = "https://github.com/sensorfu/pktbuilder" categories = [ "encoding", "no-std" ] +[dev-dependencies] +proptest = { version = "1", default-features = false, features = [ "std" ] } +untrusted = "0.9" +untrustended = "0.4" + [lints.rust] deprecated-safe = { level = "deny", priority = -1 } future-incompatible = { level = "deny", priority = -1 } diff --git a/src/lib.rs b/src/lib.rs index 09ea929..aa0088b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,6 +278,132 @@ impl<'a> Builder<'a> { Ok(self) } + /// Puts `data` in big endian byte order into position pointed by `offset`. + /// Returns [Error] if `offset` is incorrect. Does not change the current + /// position on buffer. + pub fn put_u48_be(&mut self, offset: Offset, data: u64) -> Result<&mut Self, Error> { + debug_assert!(data >> 48 == 0, "expected 48bit data"); + self.check_from_idx(offset, 6)?; + let bytes = data.to_be_bytes(); + self.buf[offset] = bytes[2]; + self.buf[offset + 1] = bytes[3]; + self.buf[offset + 2] = bytes[4]; + self.buf[offset + 3] = bytes[5]; + self.buf[offset + 4] = bytes[6]; + self.buf[offset + 5] = bytes[7]; + Ok(self) + } + + /// Adds `data` in big endian byte order into this buffer. + pub fn add_u48_be(&mut self, data: u64) -> Result<&mut Self, Error> { + self.put_u48_be(self.index, data)?; + self.index += 6; + Ok(self) + } + + /// Puts `data` in little endian byte order into position pointed by `offset`. + /// Returns [Error] if `offset` is incorrect. Does not change the current + /// position on buffer. + pub fn put_u48_le(&mut self, offset: Offset, data: u64) -> Result<&mut Self, Error> { + debug_assert!(data >> 48 == 0, "expected 48bit data"); + self.check_from_idx(offset, 6)?; + let bytes = data.to_le_bytes(); + self.buf[offset] = bytes[0]; + self.buf[offset + 1] = bytes[1]; + self.buf[offset + 2] = bytes[2]; + self.buf[offset + 3] = bytes[3]; + self.buf[offset + 4] = bytes[4]; + self.buf[offset + 5] = bytes[5]; + Ok(self) + } + + /// Adds `data` in little endian byte order into this buffer. + pub fn add_u48_le(&mut self, data: u64) -> Result<&mut Self, Error> { + self.put_u48_le(self.index, data)?; + self.index += 6; + Ok(self) + } + + /// Adds u64 in big endian byte order into buffer + pub fn add_u64_be(&mut self, data: u64) -> Result<&mut Self, Error> { + self.put_u64_be(self.index, data)?; + self.index += 8; + Ok(self) + } + + /// Puts `data` in big endian byte order into position pointed by `offset`. + /// Returns [Error] if `offset` is incorrect. Does not change the current + /// position on buffer. + pub fn put_u64_be(&mut self, offset: Offset, data: u64) -> Result<&mut Self, Error> { + self.check_from_idx(offset, 8)?; + let bytes = data.to_be_bytes(); + self.buf[offset.0..offset.0 + 8] + .iter_mut() + .enumerate() + .for_each(|(i, v)| *v = bytes[i]); + Ok(self) + } + + /// Adds u64 in big endian byte order into buffer + pub fn add_u64_le(&mut self, data: u64) -> Result<&mut Self, Error> { + self.put_u64_le(self.index, data)?; + self.index += 8; + Ok(self) + } + + /// Puts `data` in big endian byte order into position pointed by `offset`. + /// Returns [Error] if `offset` is incorrect. Does not change the current + /// position on buffer. + pub fn put_u64_le(&mut self, offset: Offset, data: u64) -> Result<&mut Self, Error> { + self.check_from_idx(offset, 8)?; + let bytes = data.to_le_bytes(); + self.buf[offset.0..offset.0 + 8] + .iter_mut() + .enumerate() + .for_each(|(i, v)| *v = bytes[i]); + Ok(self) + } + + /// Adds u128 in big endian byte order into buffer + pub fn add_u128_be(&mut self, data: u128) -> Result<&mut Self, Error> { + self.put_u128_be(self.index, data)?; + self.index += 16; + Ok(self) + } + + /// Puts `data` in big endian byte order into position pointed by `offset`. + /// Returns [Error] if `offset` is incorrect. Does not change the current + /// position on buffer. + pub fn put_u128_be(&mut self, offset: Offset, data: u128) -> Result<&mut Self, Error> { + self.check_from_idx(offset, 16)?; + let bytes = data.to_be_bytes(); + self.buf[offset.0..offset.0 + 16] + .iter_mut() + .enumerate() + .for_each(|(i, v)| *v = bytes[i]); + Ok(self) + } + + /// Adds u128 in big endian byte order into buffer + pub fn add_u128_le(&mut self, data: u128) -> Result<&mut Self, Error> { + self.put_u128_le(self.index, data)?; + self.index += 16; + Ok(self) + } + + /// Puts `data` in big endian byte order into position pointed by `offset`. + /// Returns [Error] if `offset` is incorrect. Does not change the current + /// position on buffer. + pub fn put_u128_le(&mut self, offset: Offset, data: u128) -> Result<&mut Self, Error> { + self.check_from_idx(offset, 16)?; + let bytes = data.to_le_bytes(); + self.buf[offset.0..offset.0 + 16] + .iter_mut() + .enumerate() + .for_each(|(i, v)| *v = bytes[i]); + Ok(self) + } + /// Adds contents of `data` into buffer. pub fn add(&mut self, data: &[u8]) -> Result<&mut Self, Error> { self.put(self.index, data)?; @@ -296,15 +422,12 @@ impl<'a> Builder<'a> { /// Adds `addr` in big endian byte order into buffer. pub fn add_ipv6_address_be(&mut self, addr: Ipv6Addr) -> Result<&mut Self, Error> { - let bytes = addr.octets(); - self.add(&bytes) + self.add_u128_be(addr.to_bits()) } /// Adds `addr` in little endian byte order into buffer. pub fn add_ipv6_address_le(&mut self, addr: Ipv6Addr) -> Result<&mut Self, Error> { - let mut bytes = addr.octets(); - bytes.reverse(); - self.add(&bytes) + self.add_u128_le(addr.to_bits()) } /// Puts the `addr` into buffer in big endian byte order starting from @@ -315,8 +438,7 @@ impl<'a> Builder<'a> { offset: Offset, addr: Ipv6Addr, ) -> Result<&mut Self, Error> { - let data = addr.octets(); - self.put(offset, &data) + self.put_u128_be(offset, addr.to_bits()) } /// Puts the `addr` into buffer in little endian byte order starting from @@ -327,22 +449,17 @@ impl<'a> Builder<'a> { offset: Offset, addr: Ipv6Addr, ) -> Result<&mut Self, Error> { - let mut data = addr.octets(); - data.reverse(); - self.put(offset, &data) + self.put_u128_le(offset, addr.to_bits()) } /// Adds `addr` in big endian byte order into buffer. pub fn add_ipv4_address_be(&mut self, addr: Ipv4Addr) -> Result<&mut Self, Error> { - let bytes = addr.octets(); - self.add(&bytes) + self.add_u32_be(addr.to_bits()) } /// Adds `addr` in little endian byte order into buffer. pub fn add_ipv4_address_le(&mut self, addr: Ipv4Addr) -> Result<&mut Self, Error> { - let mut bytes = addr.octets(); - bytes.reverse(); - self.add(&bytes) + self.add_u32_le(addr.to_bits()) } /// Puts the `addr` into buffer in big endian byte order starting from @@ -353,8 +470,7 @@ impl<'a> Builder<'a> { offset: Offset, addr: Ipv4Addr, ) -> Result<&mut Self, Error> { - let bytes = addr.octets(); - self.put(offset, &bytes) + self.put_u32_be(offset, addr.to_bits()) } /// Puts the `addr` into buffer in little endian byte order starting from @@ -365,9 +481,7 @@ impl<'a> Builder<'a> { offset: Offset, addr: Ipv4Addr, ) -> Result<&mut Self, Error> { - let mut bytes = addr.octets(); - bytes.reverse(); - self.put(offset, &bytes) + self.put_u32_le(offset, addr.to_bits()) } /// Adds `addr` in big endian byte order into buffer @@ -525,6 +639,7 @@ impl<'a> Builder<'a> { } Ok(self) } + /// Calculates a chekcsum of `len` bytes starting from `from`. The checksum /// is calculated using the function `checksum`. Resulting checksum value /// is stored to position starting from `sum_index` @@ -564,16 +679,247 @@ pub trait Buildable { #[cfg(test)] mod test { - use crate::Buildable; - - use super::Error; - - use super::Builder; use core::net::{Ipv4Addr, Ipv6Addr}; use core::panic; + use proptest::prelude::*; + use untrustended::ReaderExt; + + use crate::{Buildable, Builder, Error}; + type Result = core::result::Result<(), super::Error>; + fn err>(err: E) -> untrustended::Error { + err.into() + } + + proptest::proptest! { + #[test] + fn proptest_u8(value: u8) { + let mut data = [0xCD; 3]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_byte(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_byte().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u16_be(value: u16) { + let mut data = [0xCD; 4]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u16_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u16be().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u16_le(value: u16) { + let mut data = [0xCD; 4]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u16_le(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u16le().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u24_be(value in ..2_u32.pow(24)) { + let mut data = [0xCD; 5]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u24_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u24be().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u24_le(value in ..2_u32.pow(24)) { + let mut data = [0xCD; 5]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u24_le(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u24le().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u32_be(value: u32) { + let mut data = [0xCD; 6]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u32_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u32be().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u32_le(value: u32) { + let mut data = [0xCD; 6]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u32_le(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u32le().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u48_be(value in ..2_u64.pow(48)) { + let mut data = [0xCD; 8]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u48_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u48be().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u48_le(value in ..2_u64.pow(48)) { + let mut data = [0xCD; 8]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u48_le(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u48le().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u64_be(value: u64) { + let mut data = [0xCD; 10]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u64_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u64be().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u64_le(value: u64) { + let mut data = [0xCD; 10]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u64_le(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u64le().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u128_be(value: u128) { + let mut data = [0xCD; 18]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u128_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u128be().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_u128_le(value: u128) { + let mut data = [0xCD; 18]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_u128_le(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_u128le().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_ipv4addr_be(value: Ipv4Addr) { + let mut data = [0xCD; 6]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_ipv4_address_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_ipv4addr().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + + #[test] + fn proptest_ipv6addr_be(value: Ipv6Addr) { + let mut data = [0xCD; 18]; + let mut builder = Builder::new(&mut data); + builder.add_byte(0x01)?.add_ipv6_address_be(value)?.add_byte(0x02)?; + assert!(builder.add_byte(0xFF).is_err()); + let value2 = untrusted::Input::from(&data).read_all(TestCaseError::fail("EndOfInput"), |r| { + assert_eq!(0x01, r.read_byte().map_err(err)?); + let ret = r.read_ipv6addr().map_err(err)?; + assert_eq!(0x02, r.read_byte().map_err(err)?); + Ok(ret) + })?; + prop_assert_eq!(value, value2); + } + } + #[test] fn test_add_byte() -> Result { let mut data = [0u8; 2]; @@ -678,6 +1024,72 @@ mod test { Ok(()) } + #[test] + fn test_add_u48() -> Result { + let mut data = [0u8; 8]; + + let mut builder = Builder::new(&mut data); + builder + .add_byte(0x01)? + .add_u48_be(0xaabbccddeeff)? + .add_byte(0x02)?; + assert_eq!(builder.len(), 8); + assert_eq!(data, [0x01, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x02]); + + builder = Builder::new(&mut data); + builder + .add_byte(0x01)? + .add_u48_le(0xaabbccddeeff)? + .add_byte(0x02)?; + assert_eq!(builder.len(), 8); + assert_eq!(data, [0x01, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x02]); + Ok(()) + } + + #[test] + fn test_add_u48_too_long() -> Result { + let mut data = [0u8; 2]; + let mut builder = Builder::new(&mut data); + assert!(builder.add_byte(0x01)?.add_u48_be(0xaabbccddeeff).is_err()); + Ok(()) + } + + #[test] + fn test_add_u64() -> Result { + let mut data = [0u8; 10]; + let mut builder = Builder::new(&mut data); + builder + .add_byte(0x01)? + .add_u64_be(0xffa1b2c3d4e5f6dd)? + .add_byte(0x02)?; + assert_eq!( + data, + [0x01, 0xff, 0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0xdd, 0x02] + ); + + builder = Builder::new(&mut data); + builder + .add_byte(0x01)? + .add_u64_le(0xffa1b2c3d4e5f6dd)? + .add_byte(0x02)?; + assert_eq!( + data, + [0x01, 0xdd, 0xf6, 0xe5, 0xd4, 0xc3, 0xb2, 0xa1, 0xff, 0x02] + ); + Ok(()) + } + + #[test] + fn test_add_u64_too_long() -> Result { + let mut data = [0u8; 8]; + let mut builder = Builder::new(&mut data); + assert!(builder + .add_byte(0x01)? + .add_u64_be(0xffa1b2c3d4e5f6dd) + .is_err()); + Ok(()) + } + #[test] fn test_add_data() -> Result { let mut data = [0u8; 6];