From 4d13b01022da70d416dce3706a47b0e2b1ccaaa0 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 6 Feb 2023 13:13:44 +0100 Subject: [PATCH] Allow ?Sized where appropriate. (#168) This commit adds a ?Sized trait bound to all types that exclusively wrap an octets sequence. In the process, it also does some cleanup here and there to improve consistency of the crate. Specifically, it includes the following noteworthy changes: o All types that wrap an octets sequence only allow unsized octets sequence types. They all have an associated function from_slice to create a reference to a value wrapping an (unsized) octets slice and method for_slice that converts a &self into such a reference. Where the latter already existed but returned a value wrapping a &[u8] (e.g., Dname<_> and Message<_>, the return type has changed accordingly. o Removed CharStr::from_bytes. Use CharStr::from_octets instead. o Message::from_octets now returns a new error type ShortMessage. o Dropped Deref impls for Dname<_>, RelativeDname<_>. o Renamed opt::KeyTag::new to opt::KeyTag::from_octets. o Renamed rdata::Txt::try_from_slice to build_from_slice. --- src/base/charstr.rs | 140 +++++++++++++++++++----------------- src/base/message.rs | 84 +++++++++++++++++----- src/base/message_builder.rs | 8 +-- src/base/name/dname.rs | 32 +++++---- src/base/name/relative.rs | 36 ++++++---- src/base/name/uncertain.rs | 13 +++- src/base/opt/mod.rs | 86 +++++++++++++++------- src/base/opt/rfc5001.rs | 80 ++++++++++++++++----- src/base/opt/rfc6975.rs | 115 ++++++++++++++++++----------- src/base/opt/rfc8145.rs | 112 ++++++++++++++++++----------- src/rdata/macros.rs | 8 ++- src/rdata/mod.rs | 26 ++----- src/rdata/rfc1035.rs | 115 +++++++++++++++++++---------- 13 files changed, 555 insertions(+), 300 deletions(-) diff --git a/src/base/charstr.rs b/src/base/charstr.rs index 13fd11c7..0fc5f8ab 100644 --- a/src/base/charstr.rs +++ b/src/base/charstr.rs @@ -27,7 +27,7 @@ use super::cmp::CanonicalOrd; use super::scan::{BadSymbol, Scanner, Symbol, SymbolCharsError}; use super::wire::{Compose, ParseError}; #[cfg(feature = "bytes")] -use bytes::{Bytes, BytesMut}; +use bytes::BytesMut; use core::{cmp, fmt, hash, ops, str}; use octseq::builder::FreezeBuilder; #[cfg(feature = "serde")] @@ -72,11 +72,8 @@ impl CharStr { where Octs: AsRef<[u8]> + Sized, { - if octets.as_ref().len() > 255 { - Err(CharStrError) - } else { - Ok(unsafe { Self::from_octets_unchecked(octets) }) - } + CharStr::check_slice(octets.as_ref())?; + Ok(unsafe { Self::from_octets_unchecked(octets) }) } /// Creates a character string from octets without length check. @@ -84,14 +81,58 @@ impl CharStr { /// # Safety /// /// The caller has to make sure that `octets` is at most 255 octets - /// long. Otherwise, the behaviour is undefined. + /// long. Otherwise, the behavior is undefined. pub unsafe fn from_octets_unchecked(octets: Octs) -> Self where Octs: Sized, { CharStr(octets) } +} +impl CharStr<[u8]> { + /// Creates a character string from an octets slice. + pub fn from_slice(slice: &[u8]) -> Result<&Self, CharStrError> { + Self::check_slice(slice)?; + Ok(unsafe { Self::from_slice_unchecked(slice) }) + } + + /// Creates a new empty character string on an octets slice. + pub fn empty_slice() -> &'static Self { + unsafe { Self::from_slice_unchecked(b"".as_ref()) } + } + + /// Creates a character string from an octets slice without checking. + /// + /// # Safety + /// + /// The caller has to make sure that `octets` is at most 255 octets + /// long. Otherwise, the behaviour is undefined. + pub unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self { + &*(slice as *const [u8] as *const Self) + } + + /// Creates a character string from a mutable slice without checking. + /// + /// # Safety + /// + /// The caller has to make sure that `octets` is at most 255 octets + /// long. Otherwise, the behaviour is undefined. + unsafe fn from_slice_mut_unchecked(slice: &mut [u8]) -> &mut Self { + &mut *(slice as *mut [u8] as *mut Self) + } + + /// Checks whether an octets slice contains a correct character string. + fn check_slice(slice: &[u8]) -> Result<(), CharStrError> { + if slice.len() > 255 { + Err(CharStrError) + } else { + Ok(()) + } + } +} + +impl CharStr { /// Creates a new empty builder for this character string type. pub fn builder() -> CharStrBuilder where @@ -123,19 +164,19 @@ impl CharStr { } /// Returns a character string atop a slice of the content. - pub fn for_slice(&self) -> CharStr<&[u8]> + pub fn for_slice(&self) -> &CharStr<[u8]> where Octs: AsRef<[u8]>, { - unsafe { CharStr::from_octets_unchecked(self.0.as_ref()) } + unsafe { CharStr::from_slice_unchecked(self.0.as_ref()) } } /// Returns a character string atop a mutable slice of the content. - pub fn for_slice_mut(&mut self) -> CharStr<&mut [u8]> + pub fn for_slice_mut(&mut self) -> &mut CharStr<[u8]> where Octs: AsMut<[u8]>, { - unsafe { CharStr::from_octets_unchecked(self.0.as_mut()) } + unsafe { CharStr::from_slice_mut_unchecked(self.0.as_mut()) } } /// Returns a reference to a slice of the character string’s data. @@ -206,36 +247,6 @@ impl CharStr { } } -#[cfg(feature = "bytes")] -#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))] -impl CharStr { - /// Creates a new character string from a bytes value. - /// - /// Returns succesfully if the bytes slice can indeed be used as a - /// character string, i.e., it is not longer than 255 bytes. - pub fn from_bytes(bytes: Bytes) -> Result { - if bytes.len() > 255 { - Err(CharStrError) - } else { - Ok(unsafe { Self::from_octets_unchecked(bytes) }) - } - } -} - -impl CharStr<[u8]> { - /// Creates a new character string from an octet slice. - /// - /// If the byte slice is longer than 255 bytes, the function will return - /// an error. - pub fn from_slice(slice: &[u8]) -> Result<&Self, CharStrError> { - if slice.len() > 255 { - Err(CharStrError) - } else { - Ok(unsafe { &*(slice as *const [u8] as *const CharStr<[u8]>) }) - } - } -} - //--- OctetsFrom impl OctetsFrom> for CharStr @@ -282,6 +293,8 @@ where } //--- Deref and AsRef +// +// No Borrow as character strings compare ignoring case. impl ops::Deref for CharStr { type Target = Octets; @@ -375,7 +388,7 @@ impl + ?Sized> hash::Hash for CharStr { } } -//--- Display +//--- Display and Debug impl + ?Sized> fmt::Display for CharStr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -404,6 +417,14 @@ impl + ?Sized> fmt::UpperHex for CharStr { } } +impl + ?Sized> fmt::Debug for CharStr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("CharStr") + .field(&format_args!("{}", self)) + .finish() + } +} + //--- IntoIterator impl> IntoIterator for CharStr { @@ -424,20 +445,13 @@ impl<'a, T: AsRef<[u8]> + ?Sized + 'a> IntoIterator for &'a CharStr { } } -//--- Debug - -impl + ?Sized> fmt::Debug for CharStr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("CharStr") - .field(&format_args!("{}", self)) - .finish() - } -} - //--- Serialize and Deserialize #[cfg(feature = "serde")] -impl + SerializeOctets> serde::Serialize for CharStr { +impl serde::Serialize for CharStr +where + T: AsRef<[u8]> + SerializeOctets + ?Sized, +{ fn serialize( &self, serializer: S, @@ -775,7 +789,7 @@ pub struct CharStrError; impl fmt::Display for CharStrError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("illegal character string") + f.write_str("long character string") } } @@ -863,22 +877,14 @@ mod test { } #[test] - #[cfg(feature = "bytes")] - fn from_bytes() { + fn from_octets() { assert_eq!( - CharStr::from_bytes(bytes::Bytes::from_static(b"01234")) - .unwrap() - .as_slice(), + CharStr::from_octets("01234").unwrap().as_slice(), b"01234" ); - assert_eq!( - CharStr::from_bytes(bytes::Bytes::from_static(b"")) - .unwrap() - .as_slice(), - b"" - ); - assert!(CharStr::from_bytes(vec![0; 255].into()).is_ok()); - assert!(CharStr::from_bytes(vec![0; 256].into()).is_err()); + assert_eq!(CharStr::from_octets("").unwrap().as_slice(), b""); + assert!(CharStr::from_octets(vec![0; 255]).is_ok()); + assert!(CharStr::from_octets(vec![0; 256]).is_err()); } #[test] diff --git a/src/base/message.rs b/src/base/message.rs index b691c896..0d1ac9d7 100644 --- a/src/base/message.rs +++ b/src/base/message.rs @@ -22,7 +22,7 @@ use super::wire::{Composer, ParseError}; use crate::rdata::rfc1035::Cname; use core::marker::PhantomData; use core::{fmt, mem}; -use octseq::{Octets, OctetsFrom, Parser, ShortBuf}; +use octseq::{Octets, OctetsFrom, Parser}; //------------ Message ------------------------------------------------------- @@ -156,35 +156,67 @@ pub struct Message { /// # Creation and Conversion /// -impl Message { +impl Message { /// Creates a message from an octets sequence. /// /// This fails if the slice is too short to even contain a complete /// header section. No further checks are done, though, so if this /// function returns ok, the message may still be broken with other /// methods returning errors later one. - pub fn from_octets(octets: Octs) -> Result + pub fn from_octets(octets: Octs) -> Result where - Octs: AsRef<[u8]> + Sized, + Octs: AsRef<[u8]>, { - if octets.as_ref().len() < mem::size_of::() { - Err(ShortBuf) - } else { - Ok(unsafe { Self::from_octets_unchecked(octets) }) - } + Message::check_slice(octets.as_ref())?; + Ok(unsafe { Self::from_octets_unchecked(octets) }) } /// Creates a message from a bytes value without checking. /// + /// # Safety + /// /// The methods for header access rely on the octets being at least as - /// long as a header, so this is unsafe. - pub(super) unsafe fn from_octets_unchecked(octets: Octs) -> Self - where - Octs: Sized, - { + /// long as a header. If the sequence is shorter, the behavior is + /// undefined. + pub(super) unsafe fn from_octets_unchecked(octets: Octs) -> Self { Message { octets } } +} +impl Message<[u8]> { + /// Creates a message from an octets slice. + /// + /// This fails if the slice is too short to even contain a complete + /// header section. No further checks are done, though, so if this + /// function returns ok, the message may still be broken with other + /// methods returning errors later one. + pub fn from_slice(slice: &[u8]) -> Result<&Self, ShortMessage> { + Message::check_slice(slice)?; + Ok(unsafe { Self::from_slice_unchecked(slice) }) + } + + /// Creates a message from a bytes value without checking. + /// + /// # Safety + /// + /// The methods for header access rely on the octets being at least as + /// long as a header. If the sequence is shorter, the behavior is + /// undefined. + unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self { + &*(slice as *const [u8] as *const Self) + } + + /// Checks that the slice can be used for a message. + fn check_slice(slice: &[u8]) -> Result<(), ShortMessage> { + if slice.len() < mem::size_of::() { + Err(ShortMessage(())) + } else { + Ok(()) + } + } +} + +impl Message { /// Returns a reference to the underlying octets sequence. pub fn as_octets(&self) -> &Octs { &self.octets @@ -222,9 +254,7 @@ impl Message { where Octs: AsRef<[u8]>, { - unsafe { - &*(self.octets.as_ref() as *const [u8] as *const Message<[u8]>) - } + unsafe { Message::from_slice_unchecked(self.octets.as_ref()) } } } @@ -596,6 +626,9 @@ impl Message { //--- AsRef +// Octs here can’t be ?Sized or it’ll conflict with AsRef<[u8]> below. +// But [u8] is covered by that impl anyway, so no harm done. +// impl AsRef for Message { fn as_ref(&self) -> &Octs { &self.octets @@ -931,7 +964,7 @@ impl<'a, Octs: Octets + ?Sized> RecordSection<'a, Octs> { } } -//--- Clone +//--- Clone and Copy impl<'a, Octs: ?Sized> Clone for RecordSection<'a, Octs> { fn clone(&self) -> Self { @@ -1103,6 +1136,21 @@ where //============ Error Types =================================================== +//------------ ShortMessage -------------------------------------------------- + +/// A message was too short to even contain the header. +#[derive(Clone, Copy, Debug)] +pub struct ShortMessage(()); + +impl fmt::Display for ShortMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("short message") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ShortMessage {} + //------------ CopyRecordsError ---------------------------------------------- /// An error occurrd while copying records. diff --git a/src/base/message_builder.rs b/src/base/message_builder.rs index 0c9b235a..f02db5ae 100644 --- a/src/base/message_builder.rs +++ b/src/base/message_builder.rs @@ -234,7 +234,7 @@ impl MessageBuilder { /// /// The method converts the message builder into an answer builder ready /// to receive the answer for the question. - pub fn start_answer( + pub fn start_answer( mut self, msg: &Message, rcode: Rcode, @@ -1505,12 +1505,12 @@ where /// header values of the record and push options to the record data. /// /// [`AdditionalBuilder::opt`]: struct.AdditonalBuilder.html#method.opt -pub struct OptBuilder<'a, Target: AsRef<[u8]> + AsMut<[u8]>> { +pub struct OptBuilder<'a, Target: ?Sized> { start: usize, target: &'a mut Target, } -impl<'a, Target: Composer> OptBuilder<'a, Target> { +impl<'a, Target: Composer + ?Sized> OptBuilder<'a, Target> { /// Creates a new opt builder atop an additional builder. fn new(target: &'a mut Target) -> Result { let start = target.as_ref().len(); @@ -1544,7 +1544,7 @@ impl<'a, Target: Composer> OptBuilder<'a, Target> { } /// Appends an option to the OPT record. - pub fn push( + pub fn push( &mut self, opt: &Opt, ) -> Result<(), Target::AppendError> { diff --git a/src/base/name/dname.rs b/src/base/name/dname.rs index e8123e5a..e6eac8c4 100644 --- a/src/base/name/dname.rs +++ b/src/base/name/dname.rs @@ -13,7 +13,7 @@ use super::traits::{ToDname, ToLabelIter}; use bytes::Bytes; use core::ops::{Bound, RangeBounds}; use core::str::FromStr; -use core::{cmp, fmt, hash, ops, str}; +use core::{cmp, fmt, hash, str}; use octseq::builder::{EmptyBuilder, FreezeBuilder, FromBuilder, Truncate}; use octseq::octets::{Octets, OctetsFrom}; use octseq::parse::Parser; @@ -258,11 +258,11 @@ impl Dname { } /// Returns a domain name for the octets slice of the content. - pub fn for_slice(&self) -> Dname<&[u8]> + pub fn for_slice(&self) -> &Dname<[u8]> where Octs: AsRef<[u8]>, { - unsafe { Dname::from_octets_unchecked(self.0.as_ref()) } + unsafe { Dname::from_slice_unchecked(self.0.as_ref()) } } } @@ -275,6 +275,12 @@ impl + ?Sized> Dname { pub fn is_root(&self) -> bool { self.0.as_ref().len() == 1 } + + /// Returns the length of the domain name. + #[allow(clippy::len_without_is_empty)] // never empty ... + pub fn len(&self) -> usize { + self.0.as_ref().len() + } } /// # Working with Labels @@ -495,7 +501,7 @@ impl + ?Sized> Dname { } } -impl> Dname { +impl + ?Sized> Dname { /// Splits the name into two at the given position. /// /// Returns a pair of the left and right part of the split name. @@ -531,7 +537,7 @@ impl> Dname { /// it is out of bounds. pub fn truncate(mut self, len: usize) -> RelativeDname where - Octs: Truncate, + Octs: Truncate + Sized, { self.check_index(len); self.0.truncate(len); @@ -574,7 +580,7 @@ impl> Dname { base: &N, ) -> Result, Self> where - Octs: Truncate, + Octs: Truncate + Sized, { if self.ends_with(base) { let len = self.0.as_ref().len() - usize::from(base.compose_len()); @@ -620,18 +626,16 @@ impl Dname { } } -//--- Deref and AsRef +//--- AsRef -impl ops::Deref for Dname { - type Target = Octs; - - fn deref(&self) -> &Octs { +impl AsRef for Dname { + fn as_ref(&self) -> &Octs { &self.0 } } -impl + ?Sized, T: ?Sized> AsRef for Dname { - fn as_ref(&self) -> &T { +impl + ?Sized> AsRef<[u8]> for Dname { + fn as_ref(&self) -> &[u8] { self.0.as_ref() } } @@ -807,7 +811,7 @@ impl + ?Sized> fmt::Debug for Dname { #[cfg(feature = "serde")] impl serde::Serialize for Dname where - Octs: AsRef<[u8]> + SerializeOctets, + Octs: AsRef<[u8]> + SerializeOctets + ?Sized, { fn serialize( &self, diff --git a/src/base/name/relative.rs b/src/base/name/relative.rs index f5611bd0..a64f22ab 100644 --- a/src/base/name/relative.rs +++ b/src/base/name/relative.rs @@ -12,7 +12,7 @@ use super::traits::{ToLabelIter, ToRelativeDname}; use bytes::Bytes; use core::cmp::Ordering; use core::ops::{Bound, RangeBounds}; -use core::{cmp, fmt, hash, ops}; +use core::{cmp, fmt, hash}; #[cfg(feature = "serde")] use octseq::builder::{EmptyBuilder, FromBuilder}; use octseq::builder::{FreezeBuilder, IntoBuilder, Truncate}; @@ -206,11 +206,11 @@ impl RelativeDname { } /// Returns a domain name for the octets slice of the content. - pub fn for_slice(&self) -> RelativeDname<&[u8]> + pub fn for_slice(&self) -> &RelativeDname<[u8]> where Octs: AsRef<[u8]>, { - unsafe { RelativeDname::from_octets_unchecked(self.0.as_ref()) } + unsafe { RelativeDname::from_slice_unchecked(self.0.as_ref()) } } } @@ -270,6 +270,20 @@ impl RelativeDname { } } +/// # Properties +/// +impl + ?Sized> RelativeDname { + /// Returns the length of the name. + pub fn len(&self) -> usize { + self.0.as_ref().len() + } + + /// Returns whether the name is empty. + pub fn is_empty(&self) -> bool { + self.0.as_ref().is_empty() + } +} + /// # Working with Labels /// impl + ?Sized> RelativeDname { @@ -406,7 +420,7 @@ impl + ?Sized> RelativeDname { } } -impl> RelativeDname { +impl + ?Sized> RelativeDname { /// Splits the name into two at the given position. /// /// Returns a pair of the left and right part of the split name. @@ -500,18 +514,16 @@ impl> RelativeDname { } } -//--- Deref and AsRef +//--- AsRef -impl ops::Deref for RelativeDname { - type Target = Octs; - - fn deref(&self) -> &Octs { +impl AsRef for RelativeDname { + fn as_ref(&self) -> &Octs { &self.0 } } -impl + ?Sized, T: ?Sized> AsRef for RelativeDname { - fn as_ref(&self) -> &T { +impl + ?Sized> AsRef<[u8]> for RelativeDname { + fn as_ref(&self) -> &[u8] { self.0.as_ref() } } @@ -643,7 +655,7 @@ impl + ?Sized> fmt::Debug for RelativeDname { #[cfg(feature = "serde")] impl serde::Serialize for RelativeDname where - Octs: AsRef<[u8]> + SerializeOctets, + Octs: AsRef<[u8]> + SerializeOctets + ?Sized, { fn serialize( &self, diff --git a/src/base/name/uncertain.rs b/src/base/name/uncertain.rs index 94b36d34..f5ed6151 100644 --- a/src/base/name/uncertain.rs +++ b/src/base/name/uncertain.rs @@ -320,8 +320,17 @@ where //--- AsRef -impl, T> AsRef for UncertainDname { - fn as_ref(&self) -> &T { +impl AsRef for UncertainDname { + fn as_ref(&self) -> &Octs { + match *self { + UncertainDname::Absolute(ref name) => name.as_ref(), + UncertainDname::Relative(ref name) => name.as_ref(), + } + } +} + +impl> AsRef<[u8]> for UncertainDname { + fn as_ref(&self) -> &[u8] { match *self { UncertainDname::Absolute(ref name) => name.as_ref(), UncertainDname::Relative(ref name) => name.as_ref(), diff --git a/src/base/opt/mod.rs b/src/base/opt/mod.rs index 16cb077b..244b6d92 100644 --- a/src/base/opt/mod.rs +++ b/src/base/opt/mod.rs @@ -71,7 +71,7 @@ use octseq::parse::Parser; /// [`iter`]: #method.iter /// [`OptRecord`]: struct.OptRecord.html #[derive(Clone)] -pub struct Opt { +pub struct Opt { octets: Octs, } @@ -81,13 +81,54 @@ impl> Opt { /// The function checks whether the octets contain a sequence of /// options. It does not check whether the options themselves are valid. pub fn from_octets(octets: Octs) -> Result { - let mut parser = Parser::from_ref(octets.as_ref()); + Opt::check_slice(octets.as_ref())?; + Ok(Opt { octets }) + } + + /// Parses OPT record data from the beginning of a parser. + pub fn parse<'a, Src: Octets = Octs> + ?Sized>( + parser: &mut Parser<'a, Src>, + ) -> Result { + let len = parser.remaining(); + Self::from_octets(parser.parse_octets(len)?) + } +} + +impl Opt<[u8]> { + /// Creates OPT record data from an octets slice. + pub fn from_slice(slice: &[u8]) -> Result<&Self, ParseError> { + Self::check_slice(slice)?; + Ok(unsafe { Self::from_slice_unchecked(slice) }) + } + + /// Creates OPT record data from an octets slice without checking. + /// + /// # Safety + /// + /// The caller needs to ensure that the slice contains correctly encoded + /// OPT record data. The data of the options themselves does not need to + /// be correct. + unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self { + &*(slice as *const [u8] as *const Self) + } + + /// Checks that the slice contains acceptable OPT record data. + fn check_slice(slice: &[u8]) -> Result<(), ParseError> { + let mut parser = Parser::from_ref(slice); while parser.remaining() > 0 { parser.advance(2)?; let len = parser.parse_u16()?; parser.advance(len as usize)?; } - Ok(Opt { octets }) + Ok(()) + } +} + +impl + ?Sized> Opt { + /// Returns the length of the OPT record data. + #[allow(clippy::len_without_is_empty)] // never empty. + pub fn len(&self) -> usize { + self.octets.as_ref().len() } /// Returns an iterator over options of a given type. @@ -101,13 +142,6 @@ impl> Opt { { OptIter::new(&self.octets) } - - pub fn parse<'a, Src: Octets = Octs> + ?Sized>( - parser: &mut Parser<'a, Src>, - ) -> Result { - let len = parser.remaining(); - Self::from_octets(parser.parse_octets(len)?) - } } //--- OctetsFrom @@ -127,29 +161,29 @@ where impl PartialEq> for Opt where - Octs: AsRef<[u8]>, - Other: AsRef<[u8]>, + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, { fn eq(&self, other: &Opt) -> bool { self.octets.as_ref().eq(other.octets.as_ref()) } } -impl> Eq for Opt {} +impl + ?Sized> Eq for Opt {} //--- PartialOrd and Ord impl PartialOrd> for Opt where - Octs: AsRef<[u8]>, - Other: AsRef<[u8]>, + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, { fn partial_cmp(&self, other: &Opt) -> Option { self.octets.as_ref().partial_cmp(other.octets.as_ref()) } } -impl> Ord for Opt { +impl + ?Sized> Ord for Opt { fn cmp(&self, other: &Self) -> Ordering { self.octets.as_ref().cmp(other.octets.as_ref()) } @@ -157,7 +191,7 @@ impl> Ord for Opt { //--- Hash -impl> hash::Hash for Opt { +impl + ?Sized> hash::Hash for Opt { fn hash(&self, state: &mut H) { self.octets.as_ref().hash(state) } @@ -165,7 +199,7 @@ impl> hash::Hash for Opt { //--- RecordData, ParseRecordData, and ComposeRecordData -impl RecordData for Opt { +impl RecordData for Opt { fn rtype(&self) -> Rtype { Rtype::Opt } @@ -187,7 +221,7 @@ where } } -impl> ComposeRecordData for Opt { +impl + ?Sized> ComposeRecordData for Opt { fn rdlen(&self, _compress: bool) -> Option { Some(u16::try_from(self.octets.as_ref().len()).expect("long OPT")) } @@ -209,14 +243,14 @@ impl> ComposeRecordData for Opt { //--- Display -impl> fmt::Display for Opt { +impl + ?Sized> fmt::Display for Opt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // XXX TODO Print this properly. f.write_str("OPT ...") } } -impl> fmt::Debug for Opt { +impl + ?Sized> fmt::Debug for Opt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("Opt(")?; fmt::Display::fmt(self, f)?; @@ -527,7 +561,7 @@ impl OptionHeader { /// particular option. After such an error you can continue to iterate until /// `None` indicates that you’ve reached the end of the record. #[derive(Clone, Debug)] -pub struct OptIter<'a, Octs: Octets, D: ParseOptData<'a, Octs>> { +pub struct OptIter<'a, Octs: ?Sized, D> { /// A parser for the OPT record data. parser: Parser<'a, Octs>, @@ -535,7 +569,11 @@ pub struct OptIter<'a, Octs: Octets, D: ParseOptData<'a, Octs>> { marker: PhantomData, } -impl<'a, Octs: Octets, D: ParseOptData<'a, Octs>> OptIter<'a, Octs, D> { +impl<'a, Octs, D> OptIter<'a, Octs, D> +where + Octs: Octets + ?Sized, + D: ParseOptData<'a, Octs>, +{ /// Creates an iterator from a reference to the OPT record data. fn new(octets: &'a Octs) -> Self { OptIter { @@ -565,7 +603,7 @@ impl<'a, Octs: Octets, D: ParseOptData<'a, Octs>> OptIter<'a, Octs, D> { impl<'a, Octs, Data> Iterator for OptIter<'a, Octs, Data> where - Octs: Octets, + Octs: Octets + ?Sized, Data: ParseOptData<'a, Octs>, { type Item = Result; diff --git a/src/base/opt/rfc5001.rs b/src/base/opt/rfc5001.rs index 0c73f0fd..659a3bbc 100644 --- a/src/base/opt/rfc5001.rs +++ b/src/base/opt/rfc5001.rs @@ -17,7 +17,7 @@ use core::cmp::Ordering; /// /// Specified in RFC 5001. #[derive(Clone, Copy, Debug)] -pub struct Nsid { +pub struct Nsid { octets: Octs, } @@ -32,47 +32,83 @@ impl Nsid { let len = parser.remaining(); parser.parse_octets(len).map(Nsid::from_octets).map_err(Into::into) } +} +impl Nsid<[u8]> { + pub fn from_slice(slice: &[u8]) -> &Self { + unsafe { &*(slice as *const [u8] as *const Self) } + } + + pub fn from_slice_mut(slice: &mut [u8]) -> &mut Self { + unsafe { &mut *(slice as *mut [u8] as *mut Self) } + } +} + +impl Nsid { pub fn as_octets(&self) -> &Octs { &self.octets } - pub fn into_octets(self) -> Octs { + pub fn into_octets(self) -> Octs + where + Octs: Sized, + { self.octets } pub fn as_slice(&self) -> &[u8] - where Octs: AsRef<[u8]> { + where + Octs: AsRef<[u8]>, + { self.octets.as_ref() } pub fn as_slice_mut(&mut self) -> &mut [u8] - where Octs: AsMut<[u8]> { + where + Octs: AsMut<[u8]>, + { self.octets.as_mut() } + + pub fn for_slice(&self) -> &Nsid<[u8]> + where + Octs: AsRef<[u8]> + { + Nsid::from_slice(self.octets.as_ref()) + } + + pub fn for_slice_mut(&mut self) -> &mut Nsid<[u8]> + where + Octs: AsMut<[u8]> + { + Nsid::from_slice_mut(self.octets.as_mut()) + } } //--- AsRef, AsMut, Borrow, BorrowMut -impl> AsRef<[u8]> for Nsid { +impl + ?Sized> AsRef<[u8]> for Nsid { fn as_ref(&self) -> &[u8] { self.as_slice() } } -impl> AsMut<[u8]> for Nsid { +impl + ?Sized> AsMut<[u8]> for Nsid { fn as_mut(&mut self) -> &mut [u8] { self.as_slice_mut() } } -impl> borrow::Borrow<[u8]> for Nsid { +impl + ?Sized> borrow::Borrow<[u8]> for Nsid { fn borrow(&self) -> &[u8] { self.as_slice() } } -impl + AsRef<[u8]>> borrow::BorrowMut<[u8]> for Nsid { +impl borrow::BorrowMut<[u8]> for Nsid +where + Octs: AsMut<[u8]> + AsRef<[u8]> + ?Sized +{ fn borrow_mut(&mut self) -> &mut [u8] { self.as_slice_mut() } @@ -80,7 +116,7 @@ impl + AsRef<[u8]>> borrow::BorrowMut<[u8]> for Nsid { //--- OptData etc. -impl OptData for Nsid { +impl OptData for Nsid { fn code(&self) -> OptionCode { OptionCode::Nsid } @@ -100,7 +136,7 @@ impl<'a, Octs: Octets> ParseOptData<'a, Octs> for Nsid> { } } -impl> ComposeOptData for Nsid { +impl + ?Sized> ComposeOptData for Nsid { fn compose_len(&self) -> u16 { self.octets.as_ref().len().try_into().expect("long option data") } @@ -114,7 +150,7 @@ impl> ComposeOptData for Nsid { //--- Display -impl> fmt::Display for Nsid { +impl + ?Sized> fmt::Display for Nsid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // RFC 5001 § 2.4: // | User interfaces MUST read and write the contents of the NSID @@ -132,23 +168,31 @@ impl> fmt::Display for Nsid { //--- PartialEq and Eq -impl, Other: AsRef<[u8]>> PartialEq for Nsid { +impl PartialEq for Nsid +where + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, +{ fn eq(&self, other: &Other) -> bool { self.as_slice().eq(other.as_ref()) } } -impl> Eq for Nsid { } +impl + ?Sized> Eq for Nsid { } //--- PartialOrd and Ord -impl, Other: AsRef<[u8]>> PartialOrd for Nsid { +impl PartialOrd for Nsid +where + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, +{ fn partial_cmp(&self, other: &Other) -> Option { self.as_slice().partial_cmp(other.as_ref()) } } -impl> Ord for Nsid { +impl + ?Sized> Ord for Nsid { fn cmp(&self, other: &Self) -> Ordering { self.as_slice().cmp(other.as_slice()) } @@ -156,7 +200,7 @@ impl> Ord for Nsid { //--- Hash -impl> hash::Hash for Nsid { +impl + ?Sized> hash::Hash for Nsid { fn hash(&self, state: &mut H) { self.as_slice().hash(state) } @@ -167,9 +211,9 @@ impl> hash::Hash for Nsid { impl<'a, Target: Composer> OptBuilder<'a, Target> { pub fn nsid( - &mut self, data: &impl AsRef<[u8]> + &mut self, data: &(impl AsRef<[u8]> + ?Sized) ) -> Result<(), Target::AppendError> { - self.push(&Nsid::from_octets(data.as_ref())) + self.push(Nsid::from_slice(data.as_ref())) } } diff --git a/src/base/opt/rfc6975.rs b/src/base/opt/rfc6975.rs index c37a5822..9d2c84cd 100644 --- a/src/base/opt/rfc6975.rs +++ b/src/base/opt/rfc6975.rs @@ -16,7 +16,7 @@ use core::cmp::Ordering; macro_rules! option_type { ( $name:ident, $fn:ident ) => { #[derive(Clone, Copy, Debug)] - pub struct $name { + pub struct $name { octets: Octs, } @@ -25,29 +25,6 @@ macro_rules! option_type { $name { octets } } - pub fn as_octets(&self) -> &Octs { - &self.octets - } - - pub fn into_octets(self) -> Octs { - self.octets - } - - pub fn as_slice(&self) -> &[u8] - where Octs: AsRef<[u8]> { - self.octets.as_ref() - } - - pub fn as_slice_mut(&mut self) -> &mut [u8] - where Octs: AsMut<[u8]> { - self.octets.as_mut() - } - - pub fn iter(&self) -> SecAlgsIter - where Octs: AsRef<[u8]> { - SecAlgsIter::new(self.octets.as_ref()) - } - pub fn parse<'a, Src: Octets = Octs> + ?Sized>( parser: &mut Parser<'a, Src> ) -> Result { @@ -58,21 +35,79 @@ macro_rules! option_type { } } + impl $name<[u8]> { + pub fn from_slice(slice: &[u8]) -> &Self { + unsafe { &*(slice as *const [u8] as *const Self) } + } + + pub fn from_slice_mut(slice: &mut [u8]) -> &mut Self { + unsafe { &mut *(slice as *mut [u8] as *mut Self) } + } + } + + impl $name { + pub fn as_octets(&self) -> &Octs { + &self.octets + } + + pub fn into_octets(self) -> Octs + where + Octs: Sized, + { + self.octets + } + + pub fn as_slice(&self) -> &[u8] + where + Octs: AsRef<[u8]>, + { + self.octets.as_ref() + } + + pub fn as_slice_mut(&mut self) -> &mut [u8] + where + Octs: AsMut<[u8]>, + { + self.octets.as_mut() + } + + pub fn for_slice(&self) -> &$name<[u8]> + where + Octs: AsRef<[u8]>, + { + $name::from_slice(self.octets.as_ref()) + } + + pub fn for_slice_mut(&mut self) -> &mut $name<[u8]> + where + Octs: AsMut<[u8]>, + { + $name::from_slice_mut(self.octets.as_mut()) + } + + pub fn iter(&self) -> SecAlgsIter + where + Octs: AsRef<[u8]>, + { + SecAlgsIter::new(self.octets.as_ref()) + } + } + //--- AsRef, AsMut, Borrow, BorrowMut - impl> AsRef<[u8]> for $name { + impl + ?Sized> AsRef<[u8]> for $name { fn as_ref(&self) -> &[u8] { self.as_slice() } } - impl> AsMut<[u8]> for $name { + impl + ?Sized> AsMut<[u8]> for $name { fn as_mut(&mut self) -> &mut [u8] { self.as_slice_mut() } } - impl> borrow::Borrow<[u8]> for $name { + impl + ?Sized> borrow::Borrow<[u8]> for $name { fn borrow(&self) -> &[u8] { self.as_slice() } @@ -80,7 +115,7 @@ macro_rules! option_type { impl borrow::BorrowMut<[u8]> for $name where - Octs: AsMut<[u8]> + AsRef<[u8]>, + Octs: AsMut<[u8]> + AsRef<[u8]> + ?Sized, { fn borrow_mut(&mut self) -> &mut [u8] { self.as_slice_mut() @@ -89,7 +124,7 @@ macro_rules! option_type { //--- OptData etc. - impl OptData for $name { + impl OptData for $name { fn code(&self) -> OptionCode { OptionCode::$name } @@ -110,7 +145,7 @@ macro_rules! option_type { } } - impl> ComposeOptData for $name { + impl + ?Sized> ComposeOptData for $name { fn compose_len(&self) -> u16 { self.octets.as_ref().len().try_into().expect("long option data") } @@ -125,7 +160,7 @@ macro_rules! option_type { //--- IntoIter - impl<'a, Octs: AsRef<[u8]>> IntoIterator for &'a $name { + impl<'a, Octs: AsRef<[u8]> + ?Sized> IntoIterator for &'a $name { type Item = SecAlg; type IntoIter = SecAlgsIter<'a>; @@ -136,7 +171,7 @@ macro_rules! option_type { //--- Display - impl> fmt::Display for $name { + impl + ?Sized> fmt::Display for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut first = true; @@ -156,29 +191,29 @@ macro_rules! option_type { impl PartialEq for $name where - Octs: AsRef<[u8]>, - Other: AsRef<[u8]>, + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, { fn eq(&self, other: &Other) -> bool { self.as_slice().eq(other.as_ref()) } } - impl> Eq for $name { } + impl + ?Sized> Eq for $name { } //--- PartialOrd and Ord impl PartialOrd for $name where - Octs: AsRef<[u8]>, - Other: AsRef<[u8]>, + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, { fn partial_cmp(&self, other: &Other) -> Option { self.as_slice().partial_cmp(other.as_ref()) } } - impl> Ord for $name { + impl + ?Sized> Ord for $name { fn cmp(&self, other: &Self) -> Ordering { self.as_slice().cmp(other.as_slice()) } @@ -186,7 +221,7 @@ macro_rules! option_type { //--- Hash - impl> hash::Hash for $name { + impl + ?Sized> hash::Hash for $name { fn hash(&self, state: &mut H) { self.as_slice().hash(state) } @@ -196,7 +231,7 @@ macro_rules! option_type { impl<'a, Target: Composer> OptBuilder<'a, Target> { pub fn $fn( - &mut self, octets: &impl AsRef<[u8]> + &mut self, octets: &(impl AsRef<[u8]> + ?Sized) ) -> Result<(), Target::AppendError> { self.push(&$name::from_octets(octets.as_ref())) } diff --git a/src/base/opt/rfc8145.rs b/src/base/opt/rfc8145.rs index 02e02c87..98b70875 100644 --- a/src/base/opt/rfc8145.rs +++ b/src/base/opt/rfc8145.rs @@ -15,38 +15,15 @@ use core::convert::TryInto; //------------ KeyTag ------------------------------------------------------- #[derive(Clone, Debug)] -pub struct KeyTag { +pub struct KeyTag { octets: Octs, } impl KeyTag { - pub fn new(octets: Octs) -> Self { + pub fn from_octets(octets: Octs) -> Self { KeyTag { octets } } - pub fn as_octets(&self) -> &Octs { - &self.octets - } - - pub fn into_octets(self) -> Octs { - self.octets - } - - pub fn as_slice(&self) -> &[u8] - where Octs: AsRef<[u8]> { - self.octets.as_ref() - } - - pub fn as_slice_mut(&mut self) -> &mut [u8] - where Octs: AsMut<[u8]> { - self.octets.as_mut() - } - - pub fn iter(&self) -> KeyTagIter - where Octs: AsRef<[u8]> { - KeyTagIter(self.octets.as_ref()) - } - pub fn parse<'a, Src: Octets = Octs> + ?Sized>( parser: &mut Parser<'a, Src> ) -> Result { @@ -55,32 +32,77 @@ impl KeyTag { Err(FormError::new("invalid keytag length").into()) } else { - Ok(Self::new(parser.parse_octets(len)?)) + Ok(Self::from_octets(parser.parse_octets(len)?)) } } } +impl KeyTag<[u8]> { + pub fn from_slice(slice: &[u8]) -> &Self { + unsafe { &*(slice as *const [u8] as *const Self) } + } + + pub fn from_slice_mut(slice: &mut [u8]) -> &mut Self { + unsafe { &mut *(slice as *mut [u8] as *mut Self) } + } +} + +impl KeyTag { + pub fn as_octets(&self) -> &Octs { + &self.octets + } + + pub fn into_octets(self) -> Octs + where + Octs: Sized, + { + self.octets + } + + pub fn as_slice(&self) -> &[u8] + where + Octs: AsRef<[u8]>, + { + self.octets.as_ref() + } + + pub fn as_slice_mut(&mut self) -> &mut [u8] + where + Octs: AsMut<[u8]>, + { + self.octets.as_mut() + } + + pub fn iter(&self) -> KeyTagIter + where Octs: AsRef<[u8]> { + KeyTagIter(self.octets.as_ref()) + } +} + //--- AsRef, AsMut, Borrow, BorrowMut -impl> AsRef<[u8]> for KeyTag { +impl + ?Sized> AsRef<[u8]> for KeyTag { fn as_ref(&self) -> &[u8] { self.as_slice() } } -impl> AsMut<[u8]> for KeyTag { +impl + ?Sized> AsMut<[u8]> for KeyTag { fn as_mut(&mut self) -> &mut [u8] { self.as_slice_mut() } } -impl> borrow::Borrow<[u8]> for KeyTag { +impl + ?Sized> borrow::Borrow<[u8]> for KeyTag { fn borrow(&self) -> &[u8] { self.as_slice() } } -impl + AsRef<[u8]>> borrow::BorrowMut<[u8]> for KeyTag { +impl borrow::BorrowMut<[u8]> for KeyTag +where + Octs: AsMut<[u8]> + AsRef<[u8]> + ?Sized +{ fn borrow_mut(&mut self) -> &mut [u8] { self.as_slice_mut() } @@ -88,7 +110,7 @@ impl + AsRef<[u8]>> borrow::BorrowMut<[u8]> for KeyTag { //--- OptData -impl OptData for KeyTag { +impl OptData for KeyTag { fn code(&self) -> OptionCode { OptionCode::KeyTag } @@ -108,7 +130,7 @@ impl<'a, Octs: Octets> ParseOptData<'a, Octs> for KeyTag> { } } -impl> ComposeOptData for KeyTag { +impl + ?Sized> ComposeOptData for KeyTag { fn compose_len(&self) -> u16 { self.octets.as_ref().len().try_into().expect("long option data") } @@ -123,7 +145,7 @@ impl> ComposeOptData for KeyTag { //--- IntoIterator -impl<'a, Octs: AsRef<[u8]>> IntoIterator for &'a KeyTag { +impl<'a, Octs: AsRef<[u8]> + ?Sized> IntoIterator for &'a KeyTag { type Item = u16; type IntoIter = KeyTagIter<'a>; @@ -135,7 +157,7 @@ impl<'a, Octs: AsRef<[u8]>> IntoIterator for &'a KeyTag { //--- Display -impl> fmt::Display for KeyTag { +impl + ?Sized> fmt::Display for KeyTag { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut first = true; @@ -154,23 +176,31 @@ impl> fmt::Display for KeyTag { //--- PartialEq and Eq -impl, Other: AsRef<[u8]>> PartialEq for KeyTag { +impl PartialEq for KeyTag +where + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, +{ fn eq(&self, other: &Other) -> bool { self.as_slice().eq(other.as_ref()) } } -impl> Eq for KeyTag { } +impl + ?Sized> Eq for KeyTag { } //--- PartialOrd and Ord -impl, Other: AsRef<[u8]>> PartialOrd for KeyTag { +impl PartialOrd for KeyTag +where + Octs: AsRef<[u8]> + ?Sized, + Other: AsRef<[u8]> + ?Sized, +{ fn partial_cmp(&self, other: &Other) -> Option { self.as_slice().partial_cmp(other.as_ref()) } } -impl> Ord for KeyTag { +impl + ?Sized> Ord for KeyTag { fn cmp(&self, other: &Self) -> Ordering { self.as_slice().cmp(other.as_slice()) } @@ -178,7 +208,7 @@ impl> Ord for KeyTag { //--- Hash -impl> hash::Hash for KeyTag { +impl + ?Sized> hash::Hash for KeyTag { fn hash(&self, state: &mut H) { self.as_slice().hash(state) } @@ -209,7 +239,7 @@ impl<'a> Iterator for KeyTagIter<'a> { impl<'a, Target: Composer> OptBuilder<'a, Target> { pub fn key_tag( - &mut self, tags: &impl AsRef<[u16]> + &mut self, tags: &(impl AsRef<[u16]> + ?Sized) ) -> Result<(), Target::AppendError> { self.push_raw_option( OptionCode::KeyTag, @@ -237,7 +267,7 @@ mod test { #[test] fn nsid_compose_parse() { test_option_compose_parse( - &KeyTag::new("fooo"), + &KeyTag::from_octets("fooo"), |parser| KeyTag::parse(parser) ); } diff --git a/src/rdata/macros.rs b/src/rdata/macros.rs index c961cfba..957d4321 100644 --- a/src/rdata/macros.rs +++ b/src/rdata/macros.rs @@ -8,12 +8,12 @@ macro_rules! rdata_types { $module:ident::{ $( zone { - $( $mtype:ident $( < $( $mn:ident ),* > )*, )* + $( $mtype:ident $( < $( $mn:ident ),* > ),* $(,)? )* } )* $( pseudo { - $( $ptype:ident $( < $( $pn:ident ),* > )*, )* + $( $ptype:ident $( < $( $pn:ident ),* > ),* $(,)? )* } )* @@ -26,6 +26,10 @@ macro_rules! rdata_types { }; )* + $( + pub mod $module; + )* + use crate::base::name::{ParsedDname, PushError, ToDname}; use crate::base::wire::Composer; use crate::base::rdata::ComposeRecordData; diff --git a/src/rdata/mod.rs b/src/rdata/mod.rs index e186b999..05d69b34 100644 --- a/src/rdata/mod.rs +++ b/src/rdata/mod.rs @@ -47,20 +47,10 @@ #[macro_use] mod macros; -pub mod rfc1035; -pub mod rfc2782; -pub mod rfc2845; -pub mod rfc3596; -pub mod rfc4034; -pub mod rfc5155; -pub mod rfc6672; -pub mod rfc7344; -pub mod svcb; - -// The rdata_types! macro (defined in self::macros) re-exports the record data -// types here and creates the ZoneRecordData and AllRecordData enums -// containing all record types that can appear in a zone and all record -// types that exist. +// The rdata_types! macro (defined in self::macros) defines the modules +// containing the record data types, re-exports those here, and creates the +// ZoneRecordData and AllRecordData enums containing all record types that +// can appear in a zone file and all record types that exist. // // All record data types listed here MUST have the same name as the // `Rtype` variant they implement – some of the code implemented by the macro @@ -69,10 +59,8 @@ pub mod svcb; // Add any new module here and then add all record types in that module that // can appear in zone files under "zone" and all others under "pseudo". // Your type can be generic over an octet type "O" and a domain name type "N". -// Add these as needed. -// -// Each type entry has to be followed by a comma, even the last one. The macro -// is messy enough as it is ... +// Add these as needed. Trait bounds on them differ for different methods, so +// check the bounds on ZoneRecordData and AllRecordData if there are errors. rdata_types! { rfc1035::{ zone { @@ -92,7 +80,7 @@ rdata_types! { Txt, } pseudo { - Null, + Null } } rfc2782::{ diff --git a/src/rdata/rfc1035.rs b/src/rdata/rfc1035.rs index 66b34600..6807fb6f 100644 --- a/src/rdata/rfc1035.rs +++ b/src/rdata/rfc1035.rs @@ -1553,11 +1553,11 @@ impl fmt::Display for Soa { /// /// The Txt record type is defined in RFC 1035, section 3.3.14. #[derive(Clone)] -pub struct Txt(Octs); +pub struct Txt(Octs); impl Txt { /// Creates a new Txt record from a single character string. - pub fn try_from_slice(text: &[u8]) -> Result + pub fn build_from_slice(text: &[u8]) -> Result where ::Builder: EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>, @@ -1568,19 +1568,77 @@ impl Txt { } } -impl> Txt { - /// Creates a new TXT record from its encoded content. - pub fn from_octets(octets: Octs) -> Result { - let mut tmp = octets.as_ref(); - while !tmp.is_empty() { - if tmp.len() <= tmp[0] as usize { - return Err(CharStrError); - } - tmp = &tmp[(tmp[0] as usize) + 1..]; - } - Ok(Txt(octets)) +impl Txt { + /// Creates new TXT record data from its encoded content. + pub fn from_octets(octets: Octs) -> Result + where + Octs: AsRef<[u8]> + { + Txt::check_slice(octets.as_ref())?; + Ok(unsafe { Txt::from_octets_unchecked(octets) }) } + /// Creates new TXT record data without checking. + /// + /// # Safety + /// + /// The passed octets must contain correctly encoded TXT record data, + /// that is a sequence of encoded character strings. + unsafe fn from_octets_unchecked(octets: Octs) -> Self { + Txt(octets) + } + + pub fn parse<'a, Src: Octets = Octs> + ?Sized>( + parser: &mut Parser<'a, Src>, + ) -> Result + where Octs: AsRef<[u8]> { + let len = parser.remaining(); + let text = parser.parse_octets(len)?; + let mut tmp = Parser::from_ref(text.as_ref()); + while tmp.remaining() != 0 { + CharStr::skip(&mut tmp)? + } + Ok(Txt(text)) + } + + pub fn scan>( + scanner: &mut S, + ) -> Result { + scanner.scan_charstr_entry().map(Txt) + } +} + +impl Txt<[u8]> { + /// Creates new TXT record data on an octets slice. + pub fn from_slice(slice: &[u8]) -> Result<&Self, CharStrError> { + Txt::check_slice(slice)?; + Ok(unsafe { Txt::from_slice_unchecked(slice) }) + } + + /// Creates new TXT record data on an octets slice without checking. + /// + /// # Safety + /// + /// The passed octets must contain correctly encoded TXT record data, + /// that is a sequence of encoded character strings. + unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self { + unsafe { &*(slice as *const [u8] as *const Self) } + } + + /// Checks that a slice contains correctly encoded TXT data. + fn check_slice(mut slice: &[u8]) -> Result<(), CharStrError> { + while let Some(&len) = slice.first() { + let len = usize::from(len); + if slice.len() <= len { + return Err(CharStrError); + } + slice = &slice[len + 1..]; + } + Ok(()) + } +} + +impl + ?Sized> Txt { /// Returns an iterator over the text items. /// /// The Txt format contains one or more length-delimited byte strings. @@ -1593,8 +1651,9 @@ impl> Txt { TxtCharStrIter(Parser::from_ref(self.0.as_ref())) } + /// Returns the content if it consists of a single character string. pub fn as_flat_slice(&self) -> Option<&[u8]> { - if self.0.as_ref()[0] as usize == self.0.as_ref().len() - 1 { + if usize::from(self.0.as_ref()[0]) == self.0.as_ref().len() - 1 { Some(&self.0.as_ref()[1..]) } else { None @@ -1642,28 +1701,6 @@ impl> Txt { } } -impl Txt { - pub fn scan>( - scanner: &mut S, - ) -> Result { - scanner.scan_charstr_entry().map(Txt) - } -} - -impl> Txt { - pub fn parse<'a, Src: Octets = Octs> + ?Sized>( - parser: &mut Parser<'a, Src>, - ) -> Result { - let len = parser.remaining(); - let text = parser.parse_octets(len)?; - let mut tmp = Parser::from_ref(text.as_ref()); - while tmp.remaining() != 0 { - CharStr::skip(&mut tmp)? - } - Ok(Txt(text)) - } -} - impl Txt { pub(super) fn convert_octets>( self, @@ -2287,19 +2324,19 @@ mod test { #[test] fn txt_from_slice() { let short = b"01234"; - let txt: Txt> = Txt::try_from_slice(short).unwrap(); + let txt: Txt> = Txt::build_from_slice(short).unwrap(); assert_eq!(Some(&short[..]), txt.as_flat_slice()); assert_eq!(short.to_vec(), txt.text::>()); // One full slice let full = short.repeat(51); - let txt: Txt> = Txt::try_from_slice(&full).unwrap(); + let txt: Txt> = Txt::build_from_slice(&full).unwrap(); assert_eq!(Some(&full[..]), txt.as_flat_slice()); assert_eq!(full.to_vec(), txt.text::>()); // Two slices: 255, 5 let long = short.repeat(52); - let txt: Txt> = Txt::try_from_slice(&long).unwrap(); + let txt: Txt> = Txt::build_from_slice(&long).unwrap(); assert_eq!(None, txt.as_flat_slice()); assert_eq!(long.to_vec(), txt.text::>());