domain/base/name/
uncertain.rs

1//! A domain name that can be both relative or absolute.
2//!
3//! This is a private module. Its public types are re-exported by the parent.
4
5use super::super::scan::Scanner;
6use super::super::wire::ParseError;
7use super::builder::{DnameBuilder, FromStrError, PushError};
8use super::chain::{Chain, LongChainError};
9use super::dname::Dname;
10use super::label::{Label, LabelTypeError, SplitLabelError};
11use super::relative::{DnameIter, RelativeDname};
12use super::traits::ToLabelIter;
13#[cfg(feature = "bytes")]
14use bytes::Bytes;
15use core::{fmt, hash, str};
16use octseq::builder::{
17    EmptyBuilder, FreezeBuilder, FromBuilder, IntoBuilder,
18};
19#[cfg(feature = "serde")]
20use octseq::serde::{DeserializeOctets, SerializeOctets};
21#[cfg(feature = "std")]
22use std::vec::Vec;
23
24//------------ UncertainDname ------------------------------------------------
25
26/// A domain name that may be absolute or relative.
27///
28/// This type is helpful when reading a domain name from some source where it
29/// may end up being absolute or not.
30#[derive(Clone)]
31pub enum UncertainDname<Octets> {
32    Absolute(Dname<Octets>),
33    Relative(RelativeDname<Octets>),
34}
35
36impl<Octets> UncertainDname<Octets> {
37    /// Creates a new uncertain domain name from an absolute domain name.
38    pub fn absolute(name: Dname<Octets>) -> Self {
39        UncertainDname::Absolute(name)
40    }
41
42    /// Creates a new uncertain domain name from a relative domain name.
43    pub fn relative(name: RelativeDname<Octets>) -> Self {
44        UncertainDname::Relative(name)
45    }
46
47    /// Creates a new uncertain domain name containing the root label only.
48    #[must_use]
49    pub fn root() -> Self
50    where
51        Octets: From<&'static [u8]>,
52    {
53        UncertainDname::Absolute(Dname::root())
54    }
55
56    /// Creates a new uncertain yet empty domain name.
57    #[must_use]
58    pub fn empty() -> Self
59    where
60        Octets: From<&'static [u8]>,
61    {
62        UncertainDname::Relative(RelativeDname::empty())
63    }
64
65    /// Creates a new domain name from its wire format representation.
66    ///
67    /// The returned name will correctly be identified as an absolute or
68    /// relative name.
69    pub fn from_octets(octets: Octets) -> Result<Self, UncertainDnameError>
70    where
71        Octets: AsRef<[u8]>,
72    {
73        if Self::is_slice_absolute(octets.as_ref())? {
74            Ok(UncertainDname::Absolute(unsafe {
75                Dname::from_octets_unchecked(octets)
76            }))
77        } else {
78            Ok(UncertainDname::Relative(unsafe {
79                RelativeDname::from_octets_unchecked(octets)
80            }))
81        }
82    }
83
84    /// Checks an octet slice for a name and returns whether it is absolute.
85    fn is_slice_absolute(
86        mut slice: &[u8],
87    ) -> Result<bool, UncertainDnameError> {
88        if slice.len() > Dname::MAX_LEN {
89            return Err(UncertainDnameError::LongName);
90        }
91        loop {
92            let (label, tail) = Label::split_from(slice)?;
93            if label.is_root() {
94                if tail.is_empty() {
95                    return Ok(true);
96                } else {
97                    return Err(UncertainDnameError::TrailingData);
98                }
99            }
100            if tail.is_empty() {
101                return Ok(false);
102            }
103            slice = tail;
104        }
105    }
106
107    /// Creates a domain name from a sequence of characters.
108    ///
109    /// The sequence must result in a domain name in zone file
110    /// representation. That is, its labels should be separated by dots,
111    /// while actual dots, white space and backslashes should be escaped by a
112    /// preceeding backslash, and any byte value that is not a printable
113    /// ASCII character should be encoded by a backslash followed by its
114    /// three digit decimal value.
115    ///
116    /// If Internationalized Domain Names are to be used, the labels already
117    /// need to be in punycode-encoded form.
118    ///
119    /// If the last character is a dot, the name will be absolute, otherwise
120    /// it will be relative.
121    ///
122    /// If you have a string, you can also use the `FromStr` trait, which
123    /// really does the same thing.
124    pub fn from_chars<C>(chars: C) -> Result<Self, FromStrError>
125    where
126        Octets: FromBuilder,
127        <Octets as FromBuilder>::Builder: FreezeBuilder<Octets = Octets>
128            + EmptyBuilder
129            + AsRef<[u8]>
130            + AsMut<[u8]>,
131        C: IntoIterator<Item = char>,
132    {
133        let mut builder =
134            DnameBuilder::<<Octets as FromBuilder>::Builder>::new();
135        builder.append_chars(chars)?;
136        if builder.in_label() || builder.is_empty() {
137            Ok(builder.finish().into())
138        } else {
139            Ok(builder.into_dname()?.into())
140        }
141    }
142
143    pub fn scan<S: Scanner<Dname = Dname<Octets>>>(
144        scanner: &mut S,
145    ) -> Result<Self, S::Error> {
146        scanner.scan_dname().map(UncertainDname::Absolute)
147    }
148}
149
150impl UncertainDname<&'static [u8]> {
151    /// Creates an empty relative name atop a slice reference.
152    #[must_use]
153    pub fn empty_ref() -> Self {
154        Self::empty()
155    }
156
157    /// Creates an absolute name that is the root label atop a slice reference.
158    #[must_use]
159    pub fn root_ref() -> Self {
160        Self::root()
161    }
162}
163
164#[cfg(feature = "std")]
165impl UncertainDname<Vec<u8>> {
166    /// Creates an empty relative name atop a `Vec<u8>`.
167    #[must_use]
168    pub fn empty_vec() -> Self {
169        Self::empty()
170    }
171
172    /// Creates an absolute name from the root label atop a `Vec<u8>`.
173    #[must_use]
174    pub fn root_vec() -> Self {
175        Self::root()
176    }
177}
178
179#[cfg(feature = "bytes")]
180impl UncertainDname<Bytes> {
181    /// Creates an empty relative name atop a bytes value.
182    pub fn empty_bytes() -> Self {
183        Self::empty()
184    }
185
186    /// Creates an absolute name from the root label atop a bytes value.
187    pub fn root_bytes() -> Self {
188        Self::root()
189    }
190}
191
192impl<Octets> UncertainDname<Octets> {
193    /// Returns whether the name is absolute.
194    pub fn is_absolute(&self) -> bool {
195        match *self {
196            UncertainDname::Absolute(_) => true,
197            UncertainDname::Relative(_) => false,
198        }
199    }
200
201    /// Returns whether the name is relative.
202    pub fn is_relative(&self) -> bool {
203        !self.is_absolute()
204    }
205
206    /// Returns a reference to an absolute name, if this name is absolute.
207    pub fn as_absolute(&self) -> Option<&Dname<Octets>> {
208        match *self {
209            UncertainDname::Absolute(ref name) => Some(name),
210            _ => None,
211        }
212    }
213
214    /// Returns a reference to a relative name, if the name is relative.
215    pub fn as_relative(&self) -> Option<&RelativeDname<Octets>> {
216        match *self {
217            UncertainDname::Relative(ref name) => Some(name),
218            _ => None,
219        }
220    }
221
222    /// Converts the name into an absolute name.
223    ///
224    /// If the name is relative, appends the root label to it using
225    /// [`RelativeDname::into_absolute`].
226    ///
227    /// [`RelativeDname::into_absolute`]:
228    ///     struct.RelativeDname.html#method.into_absolute
229    pub fn into_absolute(self) -> Result<Dname<Octets>, PushError>
230    where
231        Octets: AsRef<[u8]> + IntoBuilder,
232        <Octets as IntoBuilder>::Builder:
233            FreezeBuilder<Octets = Octets> + AsRef<[u8]> + AsMut<[u8]>,
234    {
235        match self {
236            UncertainDname::Absolute(name) => Ok(name),
237            UncertainDname::Relative(name) => name.into_absolute(),
238        }
239    }
240
241    /// Converts the name into an absolute name if it is absolute.
242    ///
243    /// Otherwise, returns itself as the error.
244    pub fn try_into_absolute(self) -> Result<Dname<Octets>, Self> {
245        if let UncertainDname::Absolute(name) = self {
246            Ok(name)
247        } else {
248            Err(self)
249        }
250    }
251
252    /// Converts the name into a relative name if it is relative.
253    ///
254    /// Otherwise just returns itself as the error.
255    pub fn try_into_relative(self) -> Result<RelativeDname<Octets>, Self> {
256        if let UncertainDname::Relative(name) = self {
257            Ok(name)
258        } else {
259            Err(self)
260        }
261    }
262
263    /// Returns a reference to the underlying octets sequence.
264    pub fn as_octets(&self) -> &Octets {
265        match *self {
266            UncertainDname::Absolute(ref name) => name.as_octets(),
267            UncertainDname::Relative(ref name) => name.as_octets(),
268        }
269    }
270
271    /// Returns an octets slice with the raw content of the name.
272    pub fn as_slice(&self) -> &[u8]
273    where
274        Octets: AsRef<[u8]>,
275    {
276        match *self {
277            UncertainDname::Absolute(ref name) => name.as_slice(),
278            UncertainDname::Relative(ref name) => name.as_slice(),
279        }
280    }
281
282    /// Makes an uncertain name absolute by chaining on a suffix if needed.
283    ///
284    /// The method converts the uncertain name into a chain that will
285    /// be absolute. If the name is already absolute, the chain will be the
286    /// name itself. If it is relative, if will be the concatenation of the
287    /// name and `suffix`.
288    pub fn chain<S: ToLabelIter>(
289        self,
290        suffix: S,
291    ) -> Result<Chain<Self, S>, LongChainError>
292    where
293        Octets: AsRef<[u8]>,
294    {
295        Chain::new_uncertain(self, suffix)
296    }
297}
298
299//--- From
300
301impl<Octets> From<Dname<Octets>> for UncertainDname<Octets> {
302    fn from(src: Dname<Octets>) -> Self {
303        UncertainDname::Absolute(src)
304    }
305}
306
307impl<Octets> From<RelativeDname<Octets>> for UncertainDname<Octets> {
308    fn from(src: RelativeDname<Octets>) -> Self {
309        UncertainDname::Relative(src)
310    }
311}
312
313//--- FromStr
314
315impl<Octets> str::FromStr for UncertainDname<Octets>
316where
317    Octets: FromBuilder,
318    <Octets as FromBuilder>::Builder: EmptyBuilder
319        + FreezeBuilder<Octets = Octets>
320        + AsRef<[u8]>
321        + AsMut<[u8]>,
322{
323    type Err = FromStrError;
324
325    fn from_str(s: &str) -> Result<Self, Self::Err> {
326        Self::from_chars(s.chars())
327    }
328}
329
330//--- AsRef
331
332impl<Octs> AsRef<Octs> for UncertainDname<Octs> {
333    fn as_ref(&self) -> &Octs {
334        match *self {
335            UncertainDname::Absolute(ref name) => name.as_ref(),
336            UncertainDname::Relative(ref name) => name.as_ref(),
337        }
338    }
339}
340
341impl<Octs: AsRef<[u8]>> AsRef<[u8]> for UncertainDname<Octs> {
342    fn as_ref(&self) -> &[u8] {
343        match *self {
344            UncertainDname::Absolute(ref name) => name.as_ref(),
345            UncertainDname::Relative(ref name) => name.as_ref(),
346        }
347    }
348}
349
350//--- PartialEq, and Eq
351
352impl<Octets, Other> PartialEq<UncertainDname<Other>>
353    for UncertainDname<Octets>
354where
355    Octets: AsRef<[u8]>,
356    Other: AsRef<[u8]>,
357{
358    fn eq(&self, other: &UncertainDname<Other>) -> bool {
359        use UncertainDname::*;
360
361        match (self, other) {
362            (Absolute(l), Absolute(r)) => l.eq(r),
363            (Relative(l), Relative(r)) => l.eq(r),
364            _ => false,
365        }
366    }
367}
368
369impl<Octets: AsRef<[u8]>> Eq for UncertainDname<Octets> {}
370
371//--- Hash
372
373impl<Octets: AsRef<[u8]>> hash::Hash for UncertainDname<Octets> {
374    fn hash<H: hash::Hasher>(&self, state: &mut H) {
375        for item in self.iter_labels() {
376            item.hash(state)
377        }
378    }
379}
380
381//--- ToLabelIter
382
383impl<Octs: AsRef<[u8]>> ToLabelIter for UncertainDname<Octs> {
384    type LabelIter<'a> = DnameIter<'a> where Octs: 'a;
385
386    fn iter_labels(&self) -> Self::LabelIter<'_> {
387        match *self {
388            UncertainDname::Absolute(ref name) => name.iter_labels(),
389            UncertainDname::Relative(ref name) => name.iter_labels(),
390        }
391    }
392
393    fn compose_len(&self) -> u16 {
394        match *self {
395            UncertainDname::Absolute(ref name) => name.compose_len(),
396            UncertainDname::Relative(ref name) => name.compose_len(),
397        }
398    }
399}
400
401//--- IntoIterator
402
403impl<'a, Octets: AsRef<[u8]>> IntoIterator for &'a UncertainDname<Octets> {
404    type Item = &'a Label;
405    type IntoIter = DnameIter<'a>;
406
407    fn into_iter(self) -> Self::IntoIter {
408        self.iter_labels()
409    }
410}
411
412//--- Display and Debug
413
414impl<Octets: AsRef<[u8]>> fmt::Display for UncertainDname<Octets> {
415    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416        match *self {
417            UncertainDname::Absolute(ref name) => {
418                write!(f, "{}.", name)
419            }
420            UncertainDname::Relative(ref name) => name.fmt(f),
421        }
422    }
423}
424
425impl<Octets: AsRef<[u8]>> fmt::Debug for UncertainDname<Octets> {
426    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
427        match *self {
428            UncertainDname::Absolute(ref name) => {
429                write!(f, "UncertainDname::Absolute({})", name)
430            }
431            UncertainDname::Relative(ref name) => {
432                write!(f, "UncertainDname::Relative({})", name)
433            }
434        }
435    }
436}
437
438//--- Serialize and Deserialize
439
440#[cfg(feature = "serde")]
441impl<Octets> serde::Serialize for UncertainDname<Octets>
442where
443    Octets: AsRef<[u8]> + SerializeOctets,
444{
445    fn serialize<S: serde::Serializer>(
446        &self,
447        serializer: S,
448    ) -> Result<S::Ok, S::Error> {
449        if serializer.is_human_readable() {
450            serializer.serialize_newtype_struct(
451                "UncertainDname",
452                &format_args!("{}", self),
453            )
454        } else {
455            serializer.serialize_newtype_struct(
456                "UncertainDname",
457                &self.as_octets().as_serialized_octets(),
458            )
459        }
460    }
461}
462
463#[cfg(feature = "serde")]
464impl<'de, Octets> serde::Deserialize<'de> for UncertainDname<Octets>
465where
466    Octets: FromBuilder + DeserializeOctets<'de>,
467    <Octets as FromBuilder>::Builder: EmptyBuilder
468        + FreezeBuilder<Octets = Octets>
469        + AsRef<[u8]>
470        + AsMut<[u8]>,
471{
472    fn deserialize<D: serde::Deserializer<'de>>(
473        deserializer: D,
474    ) -> Result<Self, D::Error> {
475        use core::marker::PhantomData;
476
477        struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
478
479        impl<'de, Octets> serde::de::Visitor<'de> for InnerVisitor<'de, Octets>
480        where
481            Octets: FromBuilder + DeserializeOctets<'de>,
482            <Octets as FromBuilder>::Builder: EmptyBuilder
483                + FreezeBuilder<Octets = Octets>
484                + AsRef<[u8]>
485                + AsMut<[u8]>,
486        {
487            type Value = UncertainDname<Octets>;
488
489            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
490                f.write_str("a domain name")
491            }
492
493            fn visit_str<E: serde::de::Error>(
494                self,
495                v: &str,
496            ) -> Result<Self::Value, E> {
497                use core::str::FromStr;
498
499                UncertainDname::from_str(v).map_err(E::custom)
500            }
501
502            fn visit_borrowed_bytes<E: serde::de::Error>(
503                self,
504                value: &'de [u8],
505            ) -> Result<Self::Value, E> {
506                self.0.visit_borrowed_bytes(value).and_then(|octets| {
507                    UncertainDname::from_octets(octets).map_err(E::custom)
508                })
509            }
510
511            #[cfg(feature = "std")]
512            fn visit_byte_buf<E: serde::de::Error>(
513                self,
514                value: std::vec::Vec<u8>,
515            ) -> Result<Self::Value, E> {
516                self.0.visit_byte_buf(value).and_then(|octets| {
517                    UncertainDname::from_octets(octets).map_err(E::custom)
518                })
519            }
520        }
521
522        struct NewtypeVisitor<T>(PhantomData<T>);
523
524        impl<'de, Octets> serde::de::Visitor<'de> for NewtypeVisitor<Octets>
525        where
526            Octets: FromBuilder + DeserializeOctets<'de>,
527            <Octets as FromBuilder>::Builder: EmptyBuilder
528                + FreezeBuilder<Octets = Octets>
529                + AsRef<[u8]>
530                + AsMut<[u8]>,
531        {
532            type Value = UncertainDname<Octets>;
533
534            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
535                f.write_str("a domain name")
536            }
537
538            fn visit_newtype_struct<D: serde::Deserializer<'de>>(
539                self,
540                deserializer: D,
541            ) -> Result<Self::Value, D::Error> {
542                if deserializer.is_human_readable() {
543                    deserializer
544                        .deserialize_str(InnerVisitor(Octets::visitor()))
545                } else {
546                    Octets::deserialize_with_visitor(
547                        deserializer,
548                        InnerVisitor(Octets::visitor()),
549                    )
550                }
551            }
552        }
553
554        deserializer.deserialize_newtype_struct(
555            "UncertainDname",
556            NewtypeVisitor(PhantomData),
557        )
558    }
559}
560
561//============ Error Types ===================================================
562
563//------------ UncertainDnameError -------------------------------------------
564
565/// A domain name wasn’t encoded correctly.
566#[derive(Clone, Copy, Debug, Eq, PartialEq)]
567pub enum UncertainDnameError {
568    /// The encoding contained an unknown or disallowed label type.
569    BadLabel(LabelTypeError),
570
571    /// The encoding contained a compression pointer.
572    CompressedName,
573
574    /// The name was longer than 255 octets.
575    LongName,
576
577    /// There was more data after the root label was encountered.
578    TrailingData,
579
580    /// The input ended in the middle of a label.
581    ShortInput,
582}
583
584//--- From
585
586impl From<LabelTypeError> for UncertainDnameError {
587    fn from(err: LabelTypeError) -> UncertainDnameError {
588        UncertainDnameError::BadLabel(err)
589    }
590}
591
592impl From<SplitLabelError> for UncertainDnameError {
593    fn from(err: SplitLabelError) -> UncertainDnameError {
594        match err {
595            SplitLabelError::Pointer(_) => {
596                UncertainDnameError::CompressedName
597            }
598            SplitLabelError::BadType(t) => UncertainDnameError::BadLabel(t),
599            SplitLabelError::ShortInput => UncertainDnameError::ShortInput,
600        }
601    }
602}
603
604//--- Display and Error
605
606impl fmt::Display for UncertainDnameError {
607    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
608        match *self {
609            UncertainDnameError::BadLabel(ref err) => err.fmt(f),
610            UncertainDnameError::CompressedName => {
611                f.write_str("compressed domain name")
612            }
613            UncertainDnameError::LongName => f.write_str("long domain name"),
614            UncertainDnameError::TrailingData => f.write_str("trailing data"),
615            UncertainDnameError::ShortInput => ParseError::ShortInput.fmt(f),
616        }
617    }
618}
619
620#[cfg(feature = "std")]
621impl std::error::Error for UncertainDnameError {}
622
623//============ Testing =======================================================
624
625#[cfg(test)]
626#[cfg(feature = "std")]
627mod test {
628    use super::*;
629    use std::str::FromStr;
630    use std::string::String;
631
632    #[test]
633    fn from_str() {
634        type U = UncertainDname<Vec<u8>>;
635
636        fn name(s: &str) -> U {
637            U::from_str(s).unwrap()
638        }
639
640        assert_eq!(
641            name("www.example.com").as_relative().unwrap().as_slice(),
642            b"\x03www\x07example\x03com"
643        );
644        assert_eq!(
645            name("www.example.com.").as_absolute().unwrap().as_slice(),
646            b"\x03www\x07example\x03com\0"
647        );
648
649        assert_eq!(
650            name(r"www\.example.com").as_slice(),
651            b"\x0bwww.example\x03com"
652        );
653        assert_eq!(
654            name(r"w\119w.example.com").as_slice(),
655            b"\x03www\x07example\x03com"
656        );
657        assert_eq!(
658            name(r"w\000w.example.com").as_slice(),
659            b"\x03w\0w\x07example\x03com"
660        );
661
662        assert_eq!(U::from_str(r"w\01"), Err(FromStrError::UnexpectedEnd));
663        assert_eq!(U::from_str(r"w\"), Err(FromStrError::UnexpectedEnd));
664        assert_eq!(
665            U::from_str(r"www..example.com"),
666            Err(FromStrError::EmptyLabel)
667        );
668        assert_eq!(
669            U::from_str(r"www.example.com.."),
670            Err(FromStrError::EmptyLabel)
671        );
672        assert_eq!(
673            U::from_str(r".www.example.com"),
674            Err(FromStrError::EmptyLabel)
675        );
676        assert_eq!(
677            U::from_str(r"www.\[322].example.com"),
678            Err(FromStrError::BinaryLabel)
679        );
680        assert_eq!(
681            U::from_str(r"www.\2example.com"),
682            Err(FromStrError::IllegalEscape)
683        );
684        assert_eq!(
685            U::from_str(r"www.\29example.com"),
686            Err(FromStrError::IllegalEscape)
687        );
688        assert_eq!(
689            U::from_str(r"www.\299example.com"),
690            Err(FromStrError::IllegalEscape)
691        );
692        assert_eq!(
693            U::from_str(r"www.\892example.com"),
694            Err(FromStrError::IllegalEscape)
695        );
696        assert_eq!(
697            U::from_str("www.e\0ample.com"),
698            Err(FromStrError::IllegalCharacter('\0'))
699        );
700        assert_eq!(
701            U::from_str("www.eüample.com"),
702            Err(FromStrError::IllegalCharacter('ü'))
703        );
704
705        // LongLabel
706        let mut s = String::from("www.");
707        for _ in 0..Label::MAX_LEN {
708            s.push('x');
709        }
710        s.push_str(".com");
711        assert!(U::from_str(&s).is_ok());
712        let mut s = String::from("www.");
713        for _ in 0..64 {
714            s.push('x');
715        }
716        s.push_str(".com");
717        assert_eq!(U::from_str(&s), Err(FromStrError::LongLabel));
718
719        // Long Name
720        let mut s = String::new();
721        for _ in 0..50 {
722            s.push_str("four.");
723        }
724        let mut s1 = s.clone();
725        s1.push_str("com.");
726        assert_eq!(name(&s1).as_slice().len(), 255);
727        let mut s1 = s.clone();
728        s1.push_str("com");
729        assert_eq!(name(&s1).as_slice().len(), 254);
730        let mut s1 = s.clone();
731        s1.push_str("coma.");
732        assert_eq!(U::from_str(&s1), Err(FromStrError::LongName));
733        let mut s1 = s.clone();
734        s1.push_str("coma");
735        assert_eq!(U::from_str(&s1), Err(FromStrError::LongName));
736    }
737
738    #[cfg(feature = "serde")]
739    #[test]
740    fn ser_de() {
741        use serde_test::{assert_tokens, Configure, Token};
742
743        let abs_name =
744            UncertainDname::<Vec<u8>>::from_str("www.example.com.").unwrap();
745        assert!(abs_name.is_absolute());
746
747        assert_tokens(
748            &abs_name.clone().compact(),
749            &[
750                Token::NewtypeStruct {
751                    name: "UncertainDname",
752                },
753                Token::ByteBuf(b"\x03www\x07example\x03com\0"),
754            ],
755        );
756        assert_tokens(
757            &abs_name.readable(),
758            &[
759                Token::NewtypeStruct {
760                    name: "UncertainDname",
761                },
762                Token::Str("www.example.com."),
763            ],
764        );
765
766        let rel_name =
767            UncertainDname::<Vec<u8>>::from_str("www.example.com").unwrap();
768        assert!(rel_name.is_relative());
769
770        assert_tokens(
771            &rel_name.clone().compact(),
772            &[
773                Token::NewtypeStruct {
774                    name: "UncertainDname",
775                },
776                Token::ByteBuf(b"\x03www\x07example\x03com"),
777            ],
778        );
779        assert_tokens(
780            &rel_name.readable(),
781            &[
782                Token::NewtypeStruct {
783                    name: "UncertainDname",
784                },
785                Token::Str("www.example.com"),
786            ],
787        );
788    }
789}