neli/
lib.rs

1//! # neli: Type safety for netlink
2//!
3//! ## Rationale
4//!
5//! This crate aims to be a pure Rust implementation that defines
6//! the necessary constants and wraps them in enums to distinguish
7//! between various categories of constants in the context of netlink.
8//!
9//! ## The project is broken down into the following modules:
10//! * `attr` - This defines a generic interface for netlink attributes
11//!   (both generic and routing netlink attributes).
12//! * `consts` - This is where all of the C-defined constants are
13//!   wrapped into type safe enums for use in the library.
14//! * `err` - This module contains all of the protocol and
15//!   library-level errors encountered in the code.
16//! * `genl` - This code provides parsing for the generic netlink
17//! * `iter` - This code handles iterating over received netlink
18//!   packets.
19//! * `nl` - This is the top level netlink header code that handles
20//!   the header that all netlink messages are encapsulated in.
21//! * `router` - High level API handling ACK and PID validation as well as automatic
22//!   sequence number handling.
23//! * `rtnl` - Routing netlink subsystem of the netlink protocol.
24//! * `socket` - Lower level API for use in sending and receiving messages.
25//! * `types` - Wrapper data types used in the library primarily to represent parts
26//!   of netlink messages.
27//! * `utils` - Data structures used for FFI and synchronization in socket operations.
28//!
29//! ## Design decisions
30//!
31//! This library has a range of APIs. Some APIs like [`NlSocket`][crate::socket::NlSocket]
32//! are basically just wrappers for syscalls, while higher level APIs like
33//! [`NlRouter`][crate::router::synchronous::NlRouter] provide features like ACK
34//! validation, socket PID validation, and sequence number handling.
35//!
36//! The goal of this library is completeness for handling netlink and
37//! am working to incorporate features that will make this library
38//! easier to use in all use cases. If you have a use case you
39//! would like to see supported, please open an issue on Github.
40//!
41//! ## Examples
42//!
43//! Examples of working code exist in the `examples/` subdirectory on
44//! Github. Run `cargo build --examples` to build the examples.
45//!
46//! Workflows usually follow a pattern of socket creation, and
47//! then either sending and receiving messages in request/response
48//! formats:
49//!
50//! ```
51//! use std::error::Error;
52//!
53//! use neli::{
54//!     consts::{genl::*, nl::*, socket::*},
55//!     err::RouterError,
56//!     genl::{Genlmsghdr, GenlmsghdrBuilder, Nlattr},
57//!     nl::{NlmsghdrBuilder, NlPayload},
58//!     router::synchronous::NlRouter,
59//!     types::{Buffer, GenlBuffer},
60//!     utils::Groups,
61//! };
62//!
63//! const GENL_VERSION: u8 = 1;
64//!
65//! fn request_response() -> Result<(), Box<dyn Error>> {
66//!     let (socket, _) = NlRouter::connect(
67//!         NlFamily::Generic,
68//!         None,
69//!         Groups::empty(),
70//!     )?;
71//!
72//!     let recv = socket.send::<_, _, NlTypeWrapper, Genlmsghdr<CtrlCmd, CtrlAttr>>(
73//!         GenlId::Ctrl,
74//!         NlmF::DUMP,
75//!         NlPayload::Payload(
76//!             GenlmsghdrBuilder::<_, CtrlAttr, _>::default()
77//!                 .cmd(CtrlCmd::Getfamily)
78//!                 .version(GENL_VERSION)
79//!                 .build()?
80//!         ),
81//!     )?;
82//!     
83//!     for msg in recv {
84//!         let msg = msg?;
85//!         // Do things with response here...
86//!     }
87//!     
88//!     Ok(())
89//! }
90//! ```
91//!
92//! or a subscriptions to a stream of event notifications from netlink:
93//!
94//! ```
95//! use std::error::Error;
96//!
97//! use neli::{
98//!     consts::{genl::*, nl::*, socket::*},
99//!     err::RouterError,
100//!     genl::Genlmsghdr,
101//!     router::synchronous::NlRouter,
102//!     utils::Groups,
103//! };
104//!
105//! fn subscribe_to_mcast() -> Result<(), Box<dyn Error>> {
106//!     let (s, multicast) = NlRouter::connect(
107//!         NlFamily::Generic,
108//!         None,
109//!         Groups::empty(),
110//!     )?;
111//!     let id = s.resolve_nl_mcast_group(
112//!         "my_family_name",
113//!         "my_multicast_group_name",
114//!     )?;
115//!     s.add_mcast_membership(Groups::new_groups(&[id]))?;
116//!     for next in multicast {
117//!         // Do stuff here with parsed packets...
118//!     
119//!         // like printing a debug representation of them:
120//!         println!("{:?}", next?);
121//!     }
122//!
123//!     Ok(())
124//! }
125//! ```
126//!
127//! ## Documentation
128//!
129//! Each module has been documented extensively to provide information
130//! on how to use the code contained in the module. Pull requests for
131//! documentation mistakes, updates, and rewording for clarity is a
132//! valuable contribution as this project aims to be as simple to use
133//! as possible.
134
135#![deny(missing_docs)]
136
137pub mod attr;
138pub mod connector;
139pub mod consts;
140pub mod err;
141pub mod genl;
142pub mod iter;
143pub mod nl;
144pub mod router;
145pub mod rtnl;
146pub mod socket;
147pub mod types;
148pub mod utils;
149
150use std::{
151    fmt::Debug,
152    io::{Cursor, Read, Write},
153    marker::PhantomData,
154    str,
155};
156
157use byteorder::{BigEndian, NativeEndian, ReadBytesExt};
158pub use neli_proc_macros::{FromBytes, FromBytesWithInput, Header, Size, ToBytes, neli_enum};
159
160use crate::{
161    self as neli,
162    consts::alignto,
163    err::{DeError, SerError},
164};
165
166/// A trait defining methods that apply to all netlink data
167/// structures related to sizing of data types.
168pub trait Size {
169    /// Size of the unpadded data structure. This will usually
170    /// only be unaligned for variable length types like
171    /// strings or byte buffers.
172    fn unpadded_size(&self) -> usize;
173
174    /// Get the size of the payload and align it to
175    /// the required netlink byte alignment.
176    fn padded_size(&self) -> usize {
177        alignto(self.unpadded_size())
178    }
179}
180
181/// A trait defining methods that apply to constant-sized
182/// data types related to size.
183pub trait TypeSize {
184    /// Get the size of a constant-sized data type.
185    fn type_size() -> usize;
186}
187
188/// A trait defining a netlink data structure's conversion to
189/// a byte buffer.
190pub trait ToBytes: Debug {
191    /// Takes a byte buffer and serializes the data structure into
192    /// it.
193    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError>;
194
195    /// Pad a netlink message to the appropriate alignment.
196    fn pad(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
197        let num_pad_bytes = alignto(buffer.position() as usize) - buffer.position() as usize;
198        buffer.write_all(&[0; libc::NLA_ALIGNTO as usize][..num_pad_bytes])?;
199        Ok(())
200    }
201}
202
203/// A trait defining how to convert from a byte buffer to a netlink
204/// data structure.
205pub trait FromBytes: Sized + Debug {
206    /// Takes a byte buffer and returns the deserialized data
207    /// structure.
208    fn from_bytes(buffer: &mut Cursor<impl AsRef<[u8]>>) -> Result<Self, DeError>;
209
210    /// Strip padding from a netlink message.
211    fn strip(buffer: &mut Cursor<impl AsRef<[u8]>>) -> Result<(), DeError> {
212        let num_strip_bytes = alignto(buffer.position() as usize) - buffer.position() as usize;
213        buffer.read_exact(&mut [0; libc::NLA_ALIGNTO as usize][..num_strip_bytes])?;
214        Ok(())
215    }
216}
217
218/// Takes an arbitrary input which serves as additional information
219/// for guiding the conversion from a byte buffer to a data
220/// structure. A common workflow is a data structure that has a size
221/// to determine how much more of the data in the byte buffer is
222/// part of a given data structure.
223pub trait FromBytesWithInput: Sized + Debug {
224    /// The type of the additional input.
225    type Input: Debug;
226
227    /// Takes a byte buffer and an additional input and returns
228    /// the deserialized data structure.
229    fn from_bytes_with_input(
230        buffer: &mut Cursor<impl AsRef<[u8]>>,
231        input: Self::Input,
232    ) -> Result<Self, DeError>;
233
234    /// Strip padding from a netlink message.
235    fn strip(buffer: &mut Cursor<impl AsRef<[u8]>>) -> Result<(), DeError> {
236        let num_strip_bytes = alignto(buffer.position() as usize) - buffer.position() as usize;
237        buffer.read_exact(&mut [0; libc::NLA_ALIGNTO as usize][..num_strip_bytes])?;
238        Ok(())
239    }
240}
241
242/// Takes an arbitrary input which serves as additional information
243/// for guiding the conversion from a byte buffer to a data
244/// structure. A common workflow is a data structure that has a size
245/// to determine how much more of the data in the byte buffer is
246/// part of a given data structure.
247///
248/// This trait borrows instead of copying.
249pub trait FromBytesWithInputBorrowed<'a>: Sized + Debug {
250    /// The type of the additional input.
251    type Input: Debug;
252
253    /// Takes a byte buffer and an additional input and returns
254    /// the deserialized data structure.
255    fn from_bytes_with_input(
256        buffer: &mut Cursor<&'a [u8]>,
257        input: Self::Input,
258    ) -> Result<Self, DeError>;
259
260    /// Strip padding from a netlink message.
261    fn strip(buffer: &mut Cursor<&'a [u8]>) -> Result<(), DeError> {
262        let num_strip_bytes = alignto(buffer.position() as usize) - buffer.position() as usize;
263        buffer.read_exact(&mut [0; libc::NLA_ALIGNTO as usize][..num_strip_bytes])?;
264        Ok(())
265    }
266}
267
268/// Defined for data structures that contain a header.
269pub trait Header {
270    /// Return the size in bytes of the data structure header.
271    fn header_size() -> usize;
272}
273
274macro_rules! impl_nl_int {
275    (impl__ $ty:ty) => {
276        impl $crate::Size for $ty {
277            fn unpadded_size(&self) -> usize {
278                std::mem::size_of::<$ty>()
279            }
280        }
281
282        impl $crate::TypeSize for $ty {
283            fn type_size() -> usize {
284                std::mem::size_of::<$ty>()
285            }
286        }
287
288    };
289    ($ty:ty, $read_method:ident, $write_method:ident) => {
290        impl_nl_int!(impl__ $ty);
291
292        impl $crate::ToBytes for $ty {
293            fn to_bytes(&self, buffer: &mut std::io::Cursor<Vec<u8>>) -> Result<(), $crate::err::SerError> {
294                <std::io::Cursor::<Vec<u8>> as byteorder::WriteBytesExt>::$write_method(buffer, *self)?;
295                Ok(())
296            }
297        }
298
299        impl $crate::FromBytes for $ty {
300            fn from_bytes(buffer: &mut std::io::Cursor<impl AsRef<[u8]>>) -> Result<Self, $crate::err::DeError> {
301                Ok(<std::io::Cursor<_> as byteorder::ReadBytesExt>::$read_method(buffer)?)
302            }
303        }
304    };
305    ($ty:ty, $read_method:ident, $write_method:ident, $endianness:ty) => {
306        impl_nl_int!(impl__ $ty);
307
308        impl $crate::ToBytes for $ty {
309            fn to_bytes(&self, buffer: &mut std::io::Cursor<Vec<u8>>) -> Result<(), $crate::err::SerError> {
310                <std::io::Cursor::<Vec<u8>> as byteorder::WriteBytesExt>::$write_method::<$endianness>(buffer, *self)?;
311                Ok(())
312            }
313        }
314
315        impl $crate::FromBytes for $ty {
316            fn from_bytes(buffer: &mut std::io::Cursor<impl AsRef<[u8]>>) -> Result<Self, $crate::err::DeError> {
317                Ok(<std::io::Cursor<_> as byteorder::ReadBytesExt>::$read_method::<$endianness>(buffer)?)
318            }
319        }
320    }
321}
322
323impl_nl_int!(u8, read_u8, write_u8);
324impl_nl_int!(u16, read_u16, write_u16, NativeEndian);
325impl_nl_int!(u32, read_u32, write_u32, NativeEndian);
326impl_nl_int!(u64, read_u64, write_u64, NativeEndian);
327impl_nl_int!(u128, read_u128, write_u128, NativeEndian);
328impl_nl_int!(i8, read_i8, write_i8);
329impl_nl_int!(i16, read_i16, write_i16, NativeEndian);
330impl_nl_int!(i32, read_i32, write_i32, NativeEndian);
331impl_nl_int!(i64, read_i64, write_i64, NativeEndian);
332impl_nl_int!(i128, read_i128, write_i128, NativeEndian);
333impl_nl_int!(f32, read_f32, write_f32, NativeEndian);
334impl_nl_int!(f64, read_f64, write_f64, NativeEndian);
335
336impl Size for () {
337    fn unpadded_size(&self) -> usize {
338        0
339    }
340}
341
342impl ToBytes for () {
343    fn to_bytes(&self, _: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
344        Ok(())
345    }
346}
347
348impl FromBytes for () {
349    fn from_bytes(_: &mut Cursor<impl AsRef<[u8]>>) -> Result<Self, DeError> {
350        Ok(())
351    }
352}
353
354impl FromBytesWithInput for () {
355    type Input = usize;
356
357    fn from_bytes_with_input(
358        _: &mut Cursor<impl AsRef<[u8]>>,
359        input: usize,
360    ) -> Result<Self, DeError> {
361        assert_eq!(input, 0);
362        Ok(())
363    }
364}
365
366impl<T> Size for PhantomData<T> {
367    fn unpadded_size(&self) -> usize {
368        0
369    }
370}
371
372impl<T> TypeSize for PhantomData<T> {
373    fn type_size() -> usize {
374        0
375    }
376}
377
378impl<T> ToBytes for PhantomData<T> {
379    fn to_bytes(&self, _: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
380        Ok(())
381    }
382}
383
384impl<T> FromBytes for PhantomData<T> {
385    fn from_bytes(_: &mut Cursor<impl AsRef<[u8]>>) -> Result<Self, DeError> {
386        Ok(PhantomData)
387    }
388}
389
390impl Size for &'_ str {
391    fn unpadded_size(&self) -> usize {
392        self.len() + 1
393    }
394}
395
396impl ToBytes for &'_ str {
397    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
398        buffer.write_all(self.as_bytes())?;
399        buffer.write_all(&[0])?;
400        Ok(())
401    }
402}
403
404impl<'a> FromBytesWithInputBorrowed<'a> for &'a str {
405    type Input = usize;
406
407    fn from_bytes_with_input(buffer: &mut Cursor<&'a [u8]>, input: usize) -> Result<Self, DeError> {
408        let position = buffer.position() as usize;
409        Ok(str::from_utf8(
410            &buffer.get_ref()[position..position + input],
411        )?)
412    }
413}
414
415impl Size for String {
416    fn unpadded_size(&self) -> usize {
417        self.as_str().unpadded_size()
418    }
419}
420
421impl ToBytes for String {
422    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
423        self.as_str().to_bytes(buffer)?;
424        Ok(())
425    }
426}
427
428impl FromBytesWithInput for String {
429    type Input = usize;
430
431    fn from_bytes_with_input(
432        buffer: &mut Cursor<impl AsRef<[u8]>>,
433        input: usize,
434    ) -> Result<Self, DeError> {
435        let s = String::from_utf8(
436            buffer.get_ref().as_ref()
437                [buffer.position() as usize..buffer.position() as usize + input - 1]
438                .to_vec(),
439        )?;
440        buffer.set_position(buffer.position() + input as u64);
441        Ok(s)
442    }
443}
444
445impl<const N: usize> FromBytes for [u8; N] {
446    fn from_bytes(buffer: &mut Cursor<impl AsRef<[u8]>>) -> Result<Self, DeError> {
447        let mut arr = [0u8; N];
448        buffer.read_exact(&mut arr)?;
449        Ok(arr)
450    }
451}
452
453impl Size for &'_ [u8] {
454    fn unpadded_size(&self) -> usize {
455        self.len()
456    }
457}
458
459impl<const N: usize> Size for [u8; N] {
460    fn unpadded_size(&self) -> usize {
461        N
462    }
463}
464
465impl ToBytes for &'_ [u8] {
466    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
467        buffer.write_all(self)?;
468        Ok(())
469    }
470}
471
472impl<const N: usize> ToBytes for [u8; N] {
473    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
474        buffer.write_all(self)?;
475        Ok(())
476    }
477}
478
479impl<'a> FromBytesWithInputBorrowed<'a> for &'a [u8] {
480    type Input = usize;
481
482    fn from_bytes_with_input(buffer: &mut Cursor<&'a [u8]>, input: usize) -> Result<Self, DeError> {
483        let position = buffer.position() as usize;
484        Ok(&buffer.get_ref()[position..position + input])
485    }
486}
487
488impl<T> Size for Vec<T>
489where
490    T: Size,
491{
492    fn unpadded_size(&self) -> usize {
493        self.iter()
494            .fold(0, |count, elem| count + elem.unpadded_size())
495    }
496}
497
498impl<T> ToBytes for Vec<T>
499where
500    T: ToBytes,
501{
502    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
503        for elem in self.iter() {
504            elem.to_bytes(buffer)?;
505        }
506        Ok(())
507    }
508}
509
510impl<T> FromBytesWithInput for Vec<T>
511where
512    T: FromBytes,
513{
514    type Input = usize;
515
516    fn from_bytes_with_input(
517        buffer: &mut Cursor<impl AsRef<[u8]>>,
518        input: Self::Input,
519    ) -> Result<Self, DeError> {
520        if buffer.position() as usize + input > buffer.get_ref().as_ref().len() {
521            return Err(DeError::InvalidInput(input));
522        }
523
524        let mut vec = Vec::new();
525        let orig_pos = buffer.position();
526        loop {
527            if buffer.position() as usize == orig_pos as usize + input {
528                break;
529            }
530
531            match T::from_bytes(buffer) {
532                Ok(elem) => vec.push(elem),
533                Err(e) => {
534                    buffer.set_position(orig_pos);
535                    return Err(e);
536                }
537            }
538            if buffer.position() as usize > orig_pos as usize + input {
539                buffer.set_position(orig_pos);
540                return Err(DeError::InvalidInput(input));
541            }
542        }
543        Ok(vec)
544    }
545}
546
547#[derive(Copy, Debug, Clone, PartialEq, Eq, Size)]
548/// A `u64` data type that will always be serialized as big endian
549pub struct BeU64(u64);
550
551impl BeU64 {
552    /// Create a big endian `u64` type from a native endian `u64`
553    pub fn new(v: u64) -> Self {
554        BeU64(v)
555    }
556
557    /// As native endian `u64`
558    pub fn as_ne_u64(self) -> u64 {
559        self.0
560    }
561}
562
563impl ToBytes for BeU64 {
564    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
565        buffer.write_all(&self.0.to_be_bytes() as &[u8])?;
566        Ok(())
567    }
568}
569
570impl FromBytes for BeU64 {
571    fn from_bytes(buffer: &mut Cursor<impl AsRef<[u8]>>) -> Result<Self, DeError> {
572        Ok(BeU64(buffer.read_u64::<BigEndian>()?))
573    }
574}
575
576#[cfg(test)]
577fn serialize<T>(t: &T) -> Result<Vec<u8>, SerError>
578where
579    T: ToBytes,
580{
581    let mut buffer = Cursor::new(Vec::new());
582    t.to_bytes(&mut buffer)?;
583    Ok(buffer.into_inner())
584}
585
586#[cfg(test)]
587mod test {
588    use super::*;
589
590    use env_logger::init;
591    use lazy_static::lazy_static;
592
593    lazy_static! {
594        static ref LOGGER: () = init();
595    }
596
597    #[allow(clippy::no_effect)]
598    pub fn setup() {
599        *LOGGER;
600    }
601
602    #[test]
603    fn test_nl_u8() {
604        setup();
605
606        let v = 5u8;
607        let ser_buffer = serialize(&v).unwrap();
608        assert_eq!(ser_buffer.as_slice()[0], v);
609
610        let de = u8::from_bytes(&mut Cursor::new(&[5u8] as &[u8])).unwrap();
611        assert_eq!(de, 5)
612    }
613
614    #[test]
615    fn test_nl_u16() {
616        setup();
617
618        let v = 6000u16;
619        let desired_buffer = v.to_ne_bytes();
620        let ser_buffer = serialize(&v).unwrap();
621        assert_eq!(ser_buffer.as_slice(), &desired_buffer);
622
623        let de = u16::from_bytes(&mut Cursor::new(&v.to_ne_bytes() as &[u8])).unwrap();
624        assert_eq!(de, 6000);
625    }
626
627    #[test]
628    fn test_nl_i32() {
629        setup();
630
631        let v = 600_000i32;
632        let desired_buffer = v.to_ne_bytes();
633        let ser_buffer = serialize(&v).unwrap();
634        assert_eq!(ser_buffer.as_slice(), &desired_buffer);
635
636        let de = i32::from_bytes(&mut Cursor::new(&v.to_ne_bytes() as &[u8])).unwrap();
637        assert_eq!(de, 600_000);
638
639        let v = -600_000i32;
640        let desired_buffer = v.to_ne_bytes();
641        let ser_buffer = serialize(&v).unwrap();
642        assert_eq!(ser_buffer.as_slice(), &desired_buffer);
643
644        let de = i32::from_bytes(&mut Cursor::new(&v.to_ne_bytes() as &[u8])).unwrap();
645        assert_eq!(de, -600_000)
646    }
647
648    #[test]
649    fn test_nl_u32() {
650        setup();
651
652        let v = 600_000u32;
653        let desired_buffer = v.to_ne_bytes();
654        let ser_buffer = serialize(&v).unwrap();
655        assert_eq!(ser_buffer.as_slice(), &desired_buffer);
656
657        let de = u32::from_bytes(&mut Cursor::new(&v.to_ne_bytes() as &[u8])).unwrap();
658        assert_eq!(de, 600_000)
659    }
660
661    #[test]
662    fn test_nl_u64() {
663        setup();
664
665        let v = 12_345_678_901_234u64;
666        let desired_buffer = v.to_ne_bytes();
667        let ser_buffer = serialize(&v).unwrap();
668        assert_eq!(ser_buffer.as_slice(), &desired_buffer);
669
670        let de = u64::from_bytes(&mut Cursor::new(&v.to_ne_bytes() as &[u8])).unwrap();
671        assert_eq!(de, 12_345_678_901_234);
672    }
673
674    #[test]
675    fn test_nl_u128() {
676        setup();
677
678        let v = 123_456_789_012_345_678_901_234_567_890_123_456_789u128;
679        let desired_buffer = v.to_ne_bytes();
680        let ser_buffer = serialize(&v).unwrap();
681        assert_eq!(ser_buffer.as_slice(), &desired_buffer);
682
683        let de = u128::from_bytes(&mut Cursor::new(&v.to_ne_bytes() as &[u8])).unwrap();
684        assert_eq!(de, 123_456_789_012_345_678_901_234_567_890_123_456_789);
685    }
686
687    #[test]
688    fn test_nl_be_u64() {
689        setup();
690
691        let v = 571_987_654u64;
692        let desired_buffer = v.to_be_bytes();
693        let ser_buffer = serialize(&BeU64(v)).unwrap();
694        assert_eq!(ser_buffer.as_slice(), &desired_buffer);
695
696        let de = BeU64::from_bytes(&mut Cursor::new(&v.to_be_bytes() as &[u8])).unwrap();
697        assert_eq!(de, BeU64(571_987_654));
698    }
699
700    #[test]
701    fn test_nl_vec() {
702        setup();
703
704        let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
705        let ser_buffer = serialize(&vec).unwrap();
706        assert_eq!(vec.as_slice(), ser_buffer.as_slice());
707
708        let v: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9];
709        let de = Vec::<u8>::from_bytes_with_input(&mut Cursor::new(v), 9).unwrap();
710        assert_eq!(vec, de.as_slice());
711    }
712
713    #[test]
714    fn test_nl_string() {
715        setup();
716
717        let s = "AAAAA".to_string();
718        let desired_s = "AAAAA\0";
719        let ser_buffer = serialize(&s).unwrap();
720        assert_eq!(desired_s.as_bytes(), ser_buffer.as_slice());
721
722        let de_s = "AAAAA".to_string();
723        let de = String::from_bytes_with_input(&mut Cursor::new(desired_s.as_bytes()), 6).unwrap();
724        assert_eq!(de_s, de)
725    }
726}