simple_dns/dns/
name.rs

1use crate::{
2    bytes_buffer::BytesBuffer,
3    lib::{
4        fmt::{Debug, Display, Formatter, Result as FmtResult},
5        format, Cow, Hash, Hasher, Iter, Seek, String, ToString, TryFrom, Vec, Write,
6    },
7};
8
9use super::{WireFormat, MAX_LABEL_LENGTH, MAX_NAME_LENGTH};
10
11const POINTER_MASK: u8 = 0b1100_0000;
12const POINTER_MASK_U16: u16 = 0b1100_0000_0000_0000;
13
14// NOTE: there are no extend labels implemented today
15// const EXTENDED_LABEL: u8 = 0b0100_0000;
16// const EXTENDED_LABEL_U16: u16 = 0b0100_0000_0000_0000;
17
18/// A Name represents a domain-name, which consists of character strings separated by dots.  
19/// Each section of a name is called label  
20/// ex: `google.com` consists of two labels `google` and `com`
21///
22/// A valid name contains only alphanumeric characters, hyphen (-), underscore (_) or dots (.) and must not exceed 255 characters.
23/// Each label must not exceed 63 characters.
24///
25/// Microsoft implementation allows unicode characters in the name content.
26/// To create a name with unicode characters, use [`Name::new_unchecked`] or
27/// [`Name::new_with_labels`]
28#[derive(Eq, Clone)]
29pub struct Name<'a> {
30    labels: Vec<Label<'a>>,
31}
32
33impl<'a> Name<'a> {
34    /// Creates a new Name. Returns [`Result::<Name>::Ok`] if given `name` contents are valid.
35    pub fn new(name: &'a str) -> crate::Result<Self> {
36        let labels = LabelsIter::new(name.as_bytes())
37            .map(Label::new)
38            .collect::<Result<Vec<Label>, _>>()?;
39
40        let name = Self { labels };
41
42        if name.len() > MAX_NAME_LENGTH {
43            Err(crate::SimpleDnsError::InvalidServiceName)
44        } else {
45            Ok(name)
46        }
47    }
48
49    /// Create a new Name without checking for size limits or contents
50    pub fn new_unchecked(name: &'a str) -> Self {
51        let labels = LabelsIter::new(name.as_bytes())
52            .map(Label::new_unchecked)
53            .collect();
54
55        Self { labels }
56    }
57
58    /// Creates a new Name with given labels
59    ///
60    /// Allows construction of labels with `.` in them.
61    pub fn new_with_labels(labels: &[Label<'a>]) -> Self {
62        Self {
63            labels: labels.to_vec(),
64        }
65    }
66
67    /// Verify if name ends with .local.
68    pub fn is_link_local(&self) -> bool {
69        match self.iter().last() {
70            Some(label) => b"local".eq_ignore_ascii_case(&label.data),
71            None => false,
72        }
73    }
74
75    /// Returns an Iter of this Name Labels
76    pub fn iter(&'a self) -> Iter<'a, Label<'a>> {
77        self.labels.iter()
78    }
79
80    /// Returns true if self is a subdomain of other
81    pub fn is_subdomain_of(&self, other: &Name) -> bool {
82        self.labels.len() > other.labels.len()
83            && other
84                .iter()
85                .rev()
86                .zip(self.iter().rev())
87                .all(|(o, s)| *o == *s)
88    }
89
90    /// Transforms the inner data into its owned type
91    pub fn into_owned<'b>(self) -> Name<'b> {
92        Name {
93            labels: self.labels.into_iter().map(|l| l.into_owned()).collect(),
94        }
95    }
96
97    /// Returns the subdomain part of self, based on `domain`.
98    /// If self is not a subdomain of `domain`, returns None
99    ///
100    /// Example:
101    /// ```
102    /// # use simple_dns::Name;
103    /// let name = Name::new_unchecked("sub.domain.local");
104    /// let domain = Name::new_unchecked("domain.local");
105    ///
106    /// assert!(domain.without(&name).is_none());
107    ///
108    /// let sub = name.without(&domain).unwrap();
109    /// assert_eq!(sub.to_string(), "sub")
110    /// ```
111    pub fn without(&'_ self, domain: &Name) -> Option<Name<'_>> {
112        if self.is_subdomain_of(domain) {
113            let labels = self.labels[..self.labels.len() - domain.labels.len()].to_vec();
114
115            Some(Name { labels })
116        } else {
117            None
118        }
119    }
120
121    /// Get the labels that compose this name
122    pub fn get_labels(&'_ self) -> &'_ [Label<'a>] {
123        &self.labels[..]
124    }
125
126    fn plain_append<T: Write>(&self, out: &mut T) -> crate::Result<()> {
127        for label in self.iter() {
128            out.write_all(&[label.len() as u8])?;
129            out.write_all(&label.data)?;
130        }
131
132        out.write_all(&[0])?;
133        Ok(())
134    }
135
136    fn compress_append<T: Write + Seek>(
137        &'a self,
138        out: &mut T,
139        name_refs: &mut crate::lib::BTreeMap<&[Label<'a>], u16>,
140    ) -> crate::Result<()> {
141        for (i, label) in self.iter().enumerate() {
142            match name_refs.entry(&self.labels[i..]) {
143                crate::lib::BTreeEntry::Occupied(e) => {
144                    let p = *e.get();
145                    out.write_all(&(p | POINTER_MASK_U16).to_be_bytes())?;
146
147                    return Ok(());
148                }
149                crate::lib::BTreeEntry::Vacant(e) => {
150                    e.insert(out.stream_position()? as u16);
151                    out.write_all(&[label.len() as u8])?;
152                    out.write_all(&label.data)?;
153                }
154            }
155        }
156
157        out.write_all(&[0])?;
158        Ok(())
159    }
160
161    /// Returns `true` if the name is valid.
162    pub fn is_valid(&self) -> bool {
163        self.labels.iter().all(|label| label.is_valid())
164    }
165
166    /// Returns the bytes of each of the labels that compose this Name
167    pub fn as_bytes(&self) -> impl Iterator<Item = &[u8]> {
168        self.labels.iter().map(|label| label.as_ref())
169    }
170}
171
172impl<'a> WireFormat<'a> for Name<'a> {
173    const MINIMUM_LEN: usize = 1;
174
175    fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
176    where
177        Self: Sized,
178    {
179        // Parse labels will extract labels until it finds a 0 len label
180        // or a pointer  to another label
181        fn parse_labels<'a>(
182            data: &mut BytesBuffer<'a>,
183            name_len: &mut usize,
184            labels: &mut Vec<Label<'a>>,
185        ) -> crate::Result<Option<usize>> {
186            loop {
187                match data.get_u8()? {
188                    0 => break Ok(None),
189                    len if len & POINTER_MASK == POINTER_MASK => {
190                        let mut pointer = len as u16;
191                        pointer <<= 8;
192                        pointer += data.get_u8()? as u16;
193                        pointer &= !POINTER_MASK_U16;
194
195                        break Ok(Some(pointer as usize));
196                    }
197                    len => {
198                        *name_len += 1 + len as usize;
199
200                        // Checking the full name len to avoid circular pointers
201                        if *name_len >= MAX_NAME_LENGTH {
202                            return Err(crate::SimpleDnsError::InvalidDnsPacket);
203                        }
204
205                        if len as usize > MAX_LABEL_LENGTH {
206                            return Err(crate::SimpleDnsError::InvalidServiceLabel);
207                        }
208
209                        // Parsing allow invalid characters in the label.
210                        // However, the length of the label must be validated (above)
211                        labels.push(Label::new_unchecked(data.get_slice(len as usize)?));
212                    }
213                }
214            }
215        }
216
217        let mut labels = Vec::new();
218        let mut name_len = 0usize;
219
220        let mut pointer = parse_labels(data, &mut name_len, &mut labels)?;
221
222        let mut data = data.clone();
223        while let Some(p) = pointer {
224            // By creating a new buffer, it is possible to simplify the parse routine since
225            // the original buffer position will remain intact when iterating throught the
226            // pointers
227            data = data.new_at(p)?;
228            pointer = parse_labels(&mut data, &mut name_len, &mut labels)?;
229        }
230
231        Ok(Self { labels })
232    }
233
234    fn write_to<T: Write>(&self, out: &mut T) -> crate::Result<()> {
235        self.plain_append(out)
236    }
237
238    fn write_compressed_to<T: Write + Seek>(
239        &'a self,
240        out: &mut T,
241        name_refs: &mut crate::lib::BTreeMap<&[Label<'a>], u16>,
242    ) -> crate::Result<()> {
243        self.compress_append(out, name_refs)
244    }
245
246    fn len(&self) -> usize {
247        self.labels
248            .iter()
249            .map(|label| label.len() + 1)
250            .sum::<usize>()
251            + Self::MINIMUM_LEN
252    }
253}
254
255impl<'a> TryFrom<&'a str> for Name<'a> {
256    type Error = crate::SimpleDnsError;
257
258    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
259        Name::new(value)
260    }
261}
262
263impl<'a> From<&'a [Label<'a>]> for Name<'a> {
264    fn from(labels: &'a [Label<'a>]) -> Self {
265        Name::new_with_labels(labels)
266    }
267}
268
269impl<'a, const N: usize> From<[Label<'a>; N]> for Name<'a> {
270    fn from(labels: [Label<'a>; N]) -> Self {
271        Name::new_with_labels(&labels)
272    }
273}
274
275impl Display for Name<'_> {
276    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
277        let mut labels = self.labels.iter();
278
279        if let Some(label) = labels.next() {
280            f.write_fmt(format_args!("{label}"))?;
281        }
282
283        for label in labels {
284            f.write_fmt(format_args!(".{label}"))?;
285        }
286
287        Ok(())
288    }
289}
290
291impl Debug for Name<'_> {
292    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
293        f.debug_tuple("Name")
294            .field(&format!("{self}"))
295            .field(&format!("{}", self.len()))
296            .finish()
297    }
298}
299
300impl PartialEq for Name<'_> {
301    fn eq(&self, other: &Self) -> bool {
302        self.labels == other.labels
303    }
304}
305
306impl Hash for Name<'_> {
307    fn hash<H: Hasher>(&self, state: &mut H) {
308        self.labels.hash(state);
309    }
310}
311
312/// An iterator over the labels in a domain name
313struct LabelsIter<'a> {
314    bytes: &'a [u8],
315    current: usize,
316}
317
318impl<'a> LabelsIter<'a> {
319    fn new(bytes: &'a [u8]) -> Self {
320        Self { bytes, current: 0 }
321    }
322}
323
324impl<'a> Iterator for LabelsIter<'a> {
325    type Item = Cow<'a, [u8]>;
326
327    fn next(&mut self) -> Option<Self::Item> {
328        for i in self.current..self.bytes.len() {
329            if self.bytes[i] == b'.' {
330                let current = crate::lib::mem::replace(&mut self.current, i + 1);
331                if i - current == 0 {
332                    continue;
333                }
334                return Some(self.bytes[current..i].into());
335            }
336        }
337
338        if self.current < self.bytes.len() {
339            let current = crate::lib::mem::replace(&mut self.current, self.bytes.len());
340            Some(self.bytes[current..].into())
341        } else {
342            None
343        }
344    }
345}
346
347/// Represents a label in a domain name
348///
349/// A valid label is consists of A-Z, a-z, 0-9, and hyphen (-), and must be at most 63 characters
350/// in length.
351/// This library also considers valid any label starting with underscore (_), to be able to parse mDNS domain names.
352///
353/// Microsoft implementation allows unicode characters in the label content.
354/// To create a label with unicode characters, use [`Label::new_unchecked`]
355///
356/// The `Display` implementation uses [`std::string::String::from_utf8_lossy`] to display the
357/// label.
358#[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord)]
359pub struct Label<'a> {
360    data: Cow<'a, [u8]>,
361}
362
363impl<'a> Label<'a> {
364    /// Create a new [`Label`] if given data is valid and within the limits
365    pub fn new<T: Into<Cow<'a, [u8]>>>(data: T) -> crate::Result<Self> {
366        let label = Self::new_unchecked(data);
367        if !label.is_valid() {
368            return Err(crate::SimpleDnsError::InvalidServiceLabel);
369        }
370
371        Ok(label)
372    }
373
374    /// Create a new Label without checking for size limits or valid content.
375    /// This function can be used to create labels with unicode characters
376    pub fn new_unchecked<T: Into<Cow<'a, [u8]>>>(data: T) -> Self {
377        Self { data: data.into() }
378    }
379
380    /// Returns the length of the label
381    pub fn len(&self) -> usize {
382        self.data.len()
383    }
384
385    /// Returns true if the label is empty
386    pub fn is_empty(&self) -> bool {
387        self.data.is_empty()
388    }
389
390    /// Transforms the inner data into its owned type
391    pub fn into_owned<'b>(self) -> Label<'b> {
392        Label {
393            data: self.data.into_owned().into(),
394        }
395    }
396
397    /// Returns `true` if the label is valid.
398    pub fn is_valid(&self) -> bool {
399        if self.data.is_empty() || self.data.len() > MAX_LABEL_LENGTH {
400            return false;
401        }
402
403        if let Some(first) = self.data.first() {
404            if !first.is_ascii_alphanumeric() && *first != b'_' {
405                return false;
406            }
407        }
408
409        if !self
410            .data
411            .iter()
412            .skip(1)
413            .all(|c| c.is_ascii_alphanumeric() || *c == b'-' || *c == b'_')
414        {
415            return false;
416        }
417
418        if let Some(last) = self.data.last() {
419            if !last.is_ascii_alphanumeric() {
420                return false;
421            }
422        }
423
424        true
425    }
426}
427
428impl Display for Label<'_> {
429    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
430        let s = String::from_utf8_lossy(&self.data);
431        f.write_str(&s)
432    }
433}
434
435impl Debug for Label<'_> {
436    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
437        f.debug_struct("Label")
438            .field("data", &self.to_string())
439            .finish()
440    }
441}
442
443impl AsRef<[u8]> for Label<'_> {
444    fn as_ref(&self) -> &[u8] {
445        self.data.as_ref()
446    }
447}
448
449#[cfg(test)]
450mod tests {
451    use super::*;
452    use crate::lib::Cursor;
453    use crate::{lib::Vec, SimpleDnsError};
454
455    #[test]
456    fn construct_valid_names() {
457        assert!(Name::new("some").is_ok());
458        assert!(Name::new("some.local").is_ok());
459        assert!(Name::new("some.local.").is_ok());
460        assert!(Name::new("some-dash.local.").is_ok());
461        assert!(Name::new("_sync_miss._tcp.local").is_ok());
462        assert!(Name::new("1sync_miss._tcp.local").is_ok());
463
464        assert_eq!(Name::new_unchecked("\u{1F600}.local.").labels.len(), 2);
465    }
466
467    #[test]
468    fn label_validate() {
469        assert!(Name::new("\u{1F600}.local.").is_err());
470        assert!(Name::new("@.local.").is_err());
471        assert!(Name::new("\\.local.").is_err());
472    }
473
474    #[test]
475    fn is_link_local() {
476        assert!(!Name::new("some.example.com").unwrap().is_link_local());
477        assert!(Name::new("some.example.local.").unwrap().is_link_local());
478    }
479
480    #[test]
481    fn parse_without_compression() {
482        let mut data = BytesBuffer::new(
483            b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\x01F\x03ISI\x04ARPA\x00\x04ARPA\x00",
484        );
485        data.advance(3).unwrap();
486        let name = Name::parse(&mut data).unwrap();
487        assert_eq!("F.ISI.ARPA", name.to_string());
488
489        let name = Name::parse(&mut data).unwrap();
490        assert_eq!("FOO.F.ISI.ARPA", name.to_string());
491    }
492
493    #[test]
494    fn parse_with_compression() {
495        let mut data = BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03\x07INVALID\xc0\x1b" );
496        data.advance(3).unwrap();
497
498        let name = Name::parse(&mut data).unwrap();
499        assert_eq!("F.ISI.ARPA", name.to_string());
500
501        let name = Name::parse(&mut data).unwrap();
502        assert_eq!("FOO.F.ISI.ARPA", name.to_string());
503
504        let name = Name::parse(&mut data).unwrap();
505        assert_eq!("BAR.F.ISI.ARPA", name.to_string());
506
507        assert!(Name::parse(&mut data).is_err());
508    }
509
510    #[test]
511    fn parse_handle_circular_pointers() {
512        let mut data = BytesBuffer::new(&[249, 0, 37, 1, 1, 139, 192, 6, 1, 1, 1, 139, 192, 6]);
513        data.advance(12).unwrap();
514
515        assert_eq!(
516            Name::parse(&mut data),
517            Err(SimpleDnsError::InvalidDnsPacket)
518        );
519    }
520
521    #[test]
522    fn test_write() {
523        let mut bytes = Vec::with_capacity(30);
524        Name::new_unchecked("_srv._udp.local")
525            .write_to(&mut bytes)
526            .unwrap();
527
528        assert_eq!(b"\x04_srv\x04_udp\x05local\x00", &bytes[..]);
529
530        let mut bytes = Vec::with_capacity(30);
531        Name::new_unchecked("_srv._udp.local2.")
532            .write_to(&mut bytes)
533            .unwrap();
534
535        assert_eq!(b"\x04_srv\x04_udp\x06local2\x00", &bytes[..]);
536    }
537
538    #[test]
539    fn root_name_should_generate_no_labels() {
540        assert_eq!(Name::new_unchecked("").labels.len(), 0);
541        assert_eq!(Name::new_unchecked(".").labels.len(), 0);
542    }
543
544    #[test]
545    fn dot_sequence_should_generate_no_labels() {
546        assert_eq!(Name::new_unchecked(".....").labels.len(), 0);
547        assert_eq!(Name::new_unchecked("example.....com").labels.len(), 2);
548    }
549
550    #[test]
551    fn root_name_should_write_zero() {
552        let mut bytes = Vec::with_capacity(30);
553        Name::new_unchecked(".").write_to(&mut bytes).unwrap();
554
555        assert_eq!(b"\x00", &bytes[..]);
556    }
557
558    #[test]
559    fn append_to_vec_with_compression() {
560        let mut buf = Cursor::new(crate::lib::vec![0, 0, 0]);
561        buf.set_position(3);
562
563        let mut name_refs = Default::default();
564
565        let f_isi_arpa = Name::new_unchecked("F.ISI.ARPA");
566        f_isi_arpa
567            .write_compressed_to(&mut buf, &mut name_refs)
568            .expect("failed to add F.ISI.ARPA");
569        let foo_f_isi_arpa = Name::new_unchecked("FOO.F.ISI.ARPA");
570        foo_f_isi_arpa
571            .write_compressed_to(&mut buf, &mut name_refs)
572            .expect("failed to add FOO.F.ISI.ARPA");
573
574        Name::new_unchecked("BAR.F.ISI.ARPA")
575            .write_compressed_to(&mut buf, &mut name_refs)
576            .expect("failed to add FOO.F.ISI.ARPA");
577
578        let data = b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03";
579        assert_eq!(data[..], buf.get_ref()[..]);
580    }
581
582    #[test]
583    fn append_to_vec_with_compression_mult_names() {
584        let mut buf = Cursor::new(Vec::new());
585        let mut name_refs = Default::default();
586
587        let isi_arpa = Name::new_unchecked("ISI.ARPA");
588        isi_arpa
589            .write_compressed_to(&mut buf, &mut name_refs)
590            .expect("failed to add ISI.ARPA");
591
592        let f_isi_arpa = Name::new_unchecked("F.ISI.ARPA");
593        f_isi_arpa
594            .write_compressed_to(&mut buf, &mut name_refs)
595            .expect("failed to add F.ISI.ARPA");
596        let foo_f_isi_arpa = Name::new_unchecked("FOO.F.ISI.ARPA");
597        foo_f_isi_arpa
598            .write_compressed_to(&mut buf, &mut name_refs)
599            .expect("failed to add F.ISI.ARPA");
600        Name::new_unchecked("BAR.F.ISI.ARPA")
601            .write_compressed_to(&mut buf, &mut name_refs)
602            .expect("failed to add F.ISI.ARPA");
603
604        let expected = b"\x03ISI\x04ARPA\x00\x01F\xc0\x00\x03FOO\xc0\x0a\x03BAR\xc0\x0a";
605        assert_eq!(expected[..], buf.get_ref()[..]);
606
607        let mut data = BytesBuffer::new(buf.get_ref());
608
609        let first = Name::parse(&mut data).unwrap();
610        assert_eq!("ISI.ARPA", first.to_string());
611        let second = Name::parse(&mut data).unwrap();
612        assert_eq!("F.ISI.ARPA", second.to_string());
613        let third = Name::parse(&mut data).unwrap();
614        assert_eq!("FOO.F.ISI.ARPA", third.to_string());
615        let fourth = Name::parse(&mut data).unwrap();
616        assert_eq!("BAR.F.ISI.ARPA", fourth.to_string());
617    }
618
619    #[test]
620    fn ensure_different_domains_are_not_compressed() {
621        let mut buf = Cursor::new(Vec::new());
622        let mut name_refs = Default::default();
623
624        let foo_bar_baz = Name::new_unchecked("FOO.BAR.BAZ");
625        foo_bar_baz
626            .write_compressed_to(&mut buf, &mut name_refs)
627            .expect("failed to add FOO.BAR.BAZ");
628
629        let foo_bar_buz = Name::new_unchecked("FOO.BAR.BUZ");
630        foo_bar_buz
631            .write_compressed_to(&mut buf, &mut name_refs)
632            .expect("failed to add FOO.BAR.BUZ");
633
634        Name::new_unchecked("FOO.BAR")
635            .write_compressed_to(&mut buf, &mut name_refs)
636            .expect("failed to add FOO.BAR");
637
638        let expected = b"\x03FOO\x03BAR\x03BAZ\x00\x03FOO\x03BAR\x03BUZ\x00\x03FOO\x03BAR\x00";
639        assert_eq!(expected[..], buf.get_ref()[..]);
640    }
641
642    #[test]
643    fn eq_other_name() -> Result<(), SimpleDnsError> {
644        assert_eq!(Name::new("example.com")?, Name::new("example.com")?);
645        assert_ne!(Name::new("some.example.com")?, Name::new("example.com")?);
646        assert_ne!(Name::new("example.co")?, Name::new("example.com")?);
647        assert_ne!(Name::new("example.com.org")?, Name::new("example.com")?);
648
649        let mut data =
650            BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03");
651        data.advance(3)?;
652        assert_eq!(Name::new("F.ISI.ARPA")?, Name::parse(&mut data)?);
653        assert_eq!(Name::new("FOO.F.ISI.ARPA")?, Name::parse(&mut data)?);
654        Ok(())
655    }
656
657    #[test]
658    fn len() -> crate::Result<()> {
659        let mut bytes = Vec::new();
660        let name_one = Name::new_unchecked("ex.com.");
661        name_one.write_to(&mut bytes)?;
662
663        assert_eq!(8, bytes.len());
664        assert_eq!(bytes.len(), name_one.len());
665        assert_eq!(8, Name::parse(&mut BytesBuffer::new(&bytes))?.len());
666
667        Ok(())
668    }
669
670    #[test]
671    fn len_compressed() -> crate::Result<()> {
672        let name_one = Name::new_unchecked("ex.com.");
673        let mut name_refs = Default::default();
674        let mut bytes = Cursor::new(Vec::new());
675        name_one.write_compressed_to(&mut bytes, &mut name_refs)?;
676        name_one.write_compressed_to(&mut bytes, &mut name_refs)?;
677
678        assert_eq!(10, bytes.get_ref().len());
679        Ok(())
680    }
681
682    #[test]
683    #[cfg(feature = "std")]
684    fn hash() -> crate::Result<()> {
685        fn get_hash(name: &Name) -> u64 {
686            let mut hasher = std::hash::DefaultHasher::default();
687            name.hash(&mut hasher);
688            hasher.finish()
689        }
690
691        let mut data =
692            BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03");
693        data.advance(3)?;
694
695        assert_eq!(
696            get_hash(&Name::new("F.ISI.ARPA")?),
697            get_hash(&Name::parse(&mut data)?)
698        );
699
700        assert_eq!(
701            get_hash(&Name::new("FOO.F.ISI.ARPA")?),
702            get_hash(&Name::parse(&mut data)?)
703        );
704
705        Ok(())
706    }
707
708    #[test]
709    fn is_subdomain_of() {
710        assert!(Name::new_unchecked("sub.example.com")
711            .is_subdomain_of(&Name::new_unchecked("example.com")));
712
713        assert!(!Name::new_unchecked("example.com")
714            .is_subdomain_of(&Name::new_unchecked("example.com")));
715
716        assert!(Name::new_unchecked("foo.sub.example.com")
717            .is_subdomain_of(&Name::new_unchecked("example.com")));
718
719        assert!(!Name::new_unchecked("example.com")
720            .is_subdomain_of(&Name::new_unchecked("example.xom")));
721
722        assert!(!Name::new_unchecked("domain.com")
723            .is_subdomain_of(&Name::new_unchecked("other.domain")));
724
725        assert!(!Name::new_unchecked("domain.com")
726            .is_subdomain_of(&Name::new_unchecked("domain.com.br")));
727    }
728
729    #[test]
730    fn subtract_domain() {
731        let domain = Name::new_unchecked("_srv3._tcp.local");
732        assert_eq!(
733            Name::new_unchecked("a._srv3._tcp.local")
734                .without(&domain)
735                .unwrap()
736                .to_string(),
737            "a"
738        );
739
740        assert!(Name::new_unchecked("unrelated").without(&domain).is_none(),);
741
742        assert_eq!(
743            Name::new_unchecked("some.longer.domain._srv3._tcp.local")
744                .without(&domain)
745                .unwrap()
746                .to_string(),
747            "some.longer.domain"
748        );
749    }
750
751    #[test]
752    fn display_invalid_label() {
753        let input = b"invalid\xF0\x90\x80label";
754        let label = Label::new_unchecked(input);
755
756        assert_eq!(label.to_string(), "invalid�label");
757    }
758}