Repository: dylanmckay/protocol (language: rust)

Issue:
Null terminated strings
I am currently implementing a protocol that has 17 bytes or things that I don't care for now and then a 0 terminated string. It is datagram based, so the length of the packet is very clear from the start.

I am not the creator of the protocol, it has been around for 20 years or so.

Is there any way I can decode that string? What happens if there are bytes left to consume?


Discussion / hints:

Fix patch:
diff --git a/protocol/src/errors.rs b/protocol/src/errors.rs
index 8cf3f29..661ee70 100644
--- a/protocol/src/errors.rs
+++ b/protocol/src/errors.rs
@@ -55,6 +55,7 @@ error_chain! {
     foreign_links {
         Io(std::io::Error);
         FromUtf8(std::string::FromUtf8Error);
+        FromNulError(std::ffi::NulError);
         TryFromIntError(TryFromIntError);
         CharTryFromError(CharTryFromError);
 
diff --git a/protocol/src/types/cstring.rs b/protocol/src/types/cstring.rs
new file mode 100644
index 0000000..6216338
--- /dev/null
+++ b/protocol/src/types/cstring.rs
@@ -0,0 +1,59 @@
+use std::ffi::CString;
+use std::io::prelude::{Read, Write};
+use {hint, util, Error, Parcel, Settings};
+
+impl Parcel for CString {
+    const TYPE_NAME: &'static str = "CString";
+
+    fn read_field(
+        read: &mut dyn Read,
+        settings: &Settings,
+        _hints: &mut hint::Hints,
+    ) -> Result<Self, Error> {
+        let mut result = Vec::new();
+        // this logic is susceptible to DoS attacks by never providing
+        //   a null character and will be fixed by
+        //   https://github.com/dylanmckay/protocol/issues/14
+        loop {
+            let c: u8 = Parcel::read(read, settings)?;
+            if c == 0x00 {
+                return Ok(CString::new(result)?);
+            }
+            result.push(c);
+        }
+    }
+
+    fn write_field(
+        &self,
+        write: &mut dyn Write,
+        settings: &Settings,
+        _hints: &mut hint::Hints,
+    ) -> Result<(), Error> {
+        util::write_items(self.clone().into_bytes_with_nul().iter(), write, settings)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::ffi::CString;
+    use std::io::Cursor;
+    use {Parcel, Settings};
+
+    #[test]
+    fn can_read_cstring() {
+        let mut data = Cursor::new([0x41, 0x42, 0x43, 0]);
+        let read_back: CString = Parcel::read(&mut data, &Settings::default()).unwrap();
+        assert_eq!(read_back, CString::new("ABC").unwrap());
+    }
+
+    #[test]
+    fn can_write_cstring() {
+        let mut buffer = Cursor::new(Vec::new());
+
+        CString::new("ABC")
+            .unwrap()
+            .write(&mut buffer, &Settings::default())
+            .unwrap();
+        assert_eq!(buffer.into_inner(), vec![0x41, 0x42, 0x43, 0]);
+    }
+}
diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs
index c5f786a..e8714b2 100644
--- a/protocol/src/types/mod.rs
+++ b/protocol/src/types/mod.rs
@@ -10,6 +10,7 @@ mod array;
 mod char;
 /// Definitions for the `std::collections` module.
 mod collections;
+mod cstring;
 mod marker;
 mod numerics;
 mod option;


Test patch: