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