1use crate::{
2 bytes_buffer::BytesBuffer,
3 lib::{
4 fmt::{Debug, Display, Formatter, Result as FmtResult},
5 format, Hash, Hasher, Iter, Seek, TryFrom, Vec, Write,
6 },
7};
8
9use super::{
10 label::{ExactLabels, Label, LabelsIter},
11 WireFormat, MAX_LABEL_LENGTH, MAX_NAME_LENGTH,
12};
13
14const POINTER_MASK: u8 = 0b1100_0000;
15const POINTER_MASK_U16: u16 = 0b1100_0000_0000_0000;
16const MAX_COMPRESSION_OFFSET: u64 = !POINTER_MASK_U16 as u64;
17
18#[derive(Eq, Clone)]
69pub struct Name<'a> {
70 labels: Vec<Label<'a>>,
71}
72
73impl<'a> Name<'a> {
74 pub fn new(name: &'a str) -> crate::Result<Self> {
81 let labels = LabelsIter::new(name.as_bytes())
82 .map(Label::new)
83 .collect::<Result<Vec<Label>, _>>()?;
84
85 let name = Self { labels };
86
87 if name.len() > MAX_NAME_LENGTH {
88 Err(crate::SimpleDnsError::InvalidServiceName)
89 } else {
90 Ok(name)
91 }
92 }
93
94 pub fn new_unchecked(name: &'a str) -> Self {
100 let labels = LabelsIter::new(name.as_bytes())
101 .map(Label::new_unchecked)
102 .collect();
103
104 Self { labels }
105 }
106
107 pub fn new_with_labels(labels: &[Label<'a>]) -> Self {
111 Self {
112 labels: labels.to_vec(),
113 }
114 }
115
116 pub fn is_link_local(&self) -> bool {
118 match self.iter().last() {
119 Some(label) => b"local".eq_ignore_ascii_case(&label.data),
120 None => false,
121 }
122 }
123
124 pub fn iter(&'a self) -> Iter<'a, Label<'a>> {
126 self.labels.iter()
127 }
128
129 pub fn is_subdomain_of(&self, other: &Name) -> bool {
131 self.labels.len() > other.labels.len()
132 && other
133 .iter()
134 .rev()
135 .zip(self.iter().rev())
136 .all(|(o, s)| *o == *s)
137 }
138
139 pub fn into_owned<'b>(self) -> Name<'b> {
141 Name {
142 labels: self.labels.into_iter().map(|l| l.into_owned()).collect(),
143 }
144 }
145
146 pub fn without(&'_ self, domain: &Name) -> Option<Name<'_>> {
161 if self.is_subdomain_of(domain) {
162 let labels = self.labels[..self.labels.len() - domain.labels.len()].to_vec();
163
164 Some(Name { labels })
165 } else {
166 None
167 }
168 }
169
170 pub fn get_labels(&'_ self) -> &'_ [Label<'a>] {
172 &self.labels[..]
173 }
174
175 fn plain_append<T: Write>(&self, out: &mut T) -> crate::Result<()> {
176 for label in self.iter() {
177 out.write_all(&[label.len() as u8])?;
178 out.write_all(&label.data)?;
179 }
180
181 out.write_all(&[0])?;
182 Ok(())
183 }
184
185 fn compress_append<T: Write + Seek>(
186 &'a self,
187 out: &mut T,
188 name_refs: &mut crate::lib::BTreeMap<crate::dns::label::ExactLabels<'a, '_>, u16>,
189 ) -> crate::Result<()> {
190 for (i, label) in self.iter().enumerate() {
191 match name_refs.entry(ExactLabels::new(&self.labels[i..])) {
192 crate::lib::BTreeEntry::Occupied(e) => {
193 let p = *e.get();
194 out.write_all(&(p | POINTER_MASK_U16).to_be_bytes())?;
195
196 return Ok(());
197 }
198 crate::lib::BTreeEntry::Vacant(e) => {
199 let pos = out.stream_position()?;
200 if pos <= MAX_COMPRESSION_OFFSET {
201 e.insert(pos as u16);
202 }
203 out.write_all(&[label.len() as u8])?;
204 out.write_all(&label.data)?;
205 }
206 }
207 }
208
209 out.write_all(&[0])?;
210 Ok(())
211 }
212
213 pub fn is_valid_hostname(&self) -> bool {
223 !self.labels.is_empty() && self.labels.iter().all(|label| label.is_valid_hostname())
224 }
225
226 pub fn is_valid_sub_type_name(&self) -> bool {
240 if self.labels.is_empty() {
242 return false;
243 }
244
245 let mut labels = self.labels.iter();
246 let (Some(subtype), Some(marker)) = (labels.next(), labels.next()) else {
247 return false;
248 };
249
250 let sub = Label::new_unchecked(&b"_sub"[..]);
251 subtype.is_valid_instance_label() && *marker == sub && Self::validate_service_name(labels)
252 }
253
254 pub fn is_valid_instance_name(&self) -> bool {
269 if self.labels.is_empty() {
272 return false;
273 }
274
275 let mut labels = self.labels.iter();
276 let Some(instance_name) = labels.next() else {
277 return false;
278 };
279
280 instance_name.is_valid_instance_label() && Self::validate_service_name(labels)
281 }
282
283 pub fn is_valid_service_name(&self) -> bool {
299 if self.labels.is_empty() {
300 return false;
301 }
302
303 Self::validate_service_name(self.labels.iter())
305 }
306
307 fn validate_service_name(mut labels: Iter<'_, Label<'_>>) -> bool {
308 let (Some(sn), Some(protocol)) = (labels.next(), labels.next()) else {
309 return false;
310 };
311
312 let tcp = Label::new_unchecked(&b"_tcp"[..]);
313 let udp = Label::new_unchecked(&b"_udp"[..]);
314
315 sn.is_valid_service_label()
316 && (*protocol == tcp || *protocol == udp)
317 && labels.all(|l| l.is_valid_hostname())
318 }
319
320 pub fn as_bytes(&self) -> impl Iterator<Item = &[u8]> {
322 self.labels.iter().map(|label| label.as_ref())
323 }
324}
325
326impl<'a> WireFormat<'a> for Name<'a> {
327 const MINIMUM_LEN: usize = 1;
328
329 fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
330 where
331 Self: Sized,
332 {
333 fn parse_labels<'a>(
336 data: &mut BytesBuffer<'a>,
337 name_len: &mut usize,
338 labels: &mut Vec<Label<'a>>,
339 ) -> crate::Result<Option<usize>> {
340 loop {
341 match data.get_u8()? {
342 0 => break Ok(None),
343 len if len & POINTER_MASK == POINTER_MASK => {
344 let mut pointer = len as u16;
345 pointer <<= 8;
346 pointer += data.get_u8()? as u16;
347 pointer &= !POINTER_MASK_U16;
348
349 break Ok(Some(pointer as usize));
350 }
351 len => {
352 *name_len += 1 + len as usize;
353
354 if *name_len >= MAX_NAME_LENGTH {
356 return Err(crate::SimpleDnsError::InvalidDnsPacket);
357 }
358
359 if len as usize > MAX_LABEL_LENGTH {
360 return Err(crate::SimpleDnsError::InvalidServiceLabel);
361 }
362
363 labels.push(Label::new_unchecked(data.get_slice(len as usize)?));
366 }
367 }
368 }
369 }
370
371 let mut labels = Vec::new();
372 let mut name_len = 0usize;
373
374 let mut pointer = parse_labels(data, &mut name_len, &mut labels)?;
375
376 let mut data = data.clone();
377 while let Some(p) = pointer {
378 data = data.new_at(p)?;
382 pointer = parse_labels(&mut data, &mut name_len, &mut labels)?;
383 }
384
385 Ok(Self { labels })
386 }
387
388 fn write_to<T: Write>(&self, out: &mut T) -> crate::Result<()> {
389 self.plain_append(out)
390 }
391
392 fn write_compressed_to<T: Write + Seek>(
393 &'a self,
394 out: &mut T,
395 name_refs: &mut crate::lib::BTreeMap<crate::dns::label::ExactLabels<'a, '_>, u16>,
396 ) -> crate::Result<()> {
397 self.compress_append(out, name_refs)
398 }
399
400 fn len(&self) -> usize {
401 self.labels
402 .iter()
403 .map(|label| label.len() + 1)
404 .sum::<usize>()
405 + Self::MINIMUM_LEN
406 }
407}
408
409impl<'a> TryFrom<&'a str> for Name<'a> {
410 type Error = crate::SimpleDnsError;
411
412 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
413 Name::new(value)
414 }
415}
416
417impl<'a> From<&'a [Label<'a>]> for Name<'a> {
418 fn from(labels: &'a [Label<'a>]) -> Self {
419 Name::new_with_labels(labels)
420 }
421}
422
423impl<'a, const N: usize> From<[Label<'a>; N]> for Name<'a> {
424 fn from(labels: [Label<'a>; N]) -> Self {
425 Name::new_with_labels(&labels)
426 }
427}
428
429impl Display for Name<'_> {
430 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
431 let mut labels = self.labels.iter();
432
433 if let Some(label) = labels.next() {
434 f.write_fmt(format_args!("{label}"))?;
435 }
436
437 for label in labels {
438 f.write_fmt(format_args!(".{label}"))?;
439 }
440
441 Ok(())
442 }
443}
444
445impl Debug for Name<'_> {
446 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
447 f.debug_tuple("Name")
448 .field(&format!("{self}"))
449 .field(&format!("{}", self.len()))
450 .finish()
451 }
452}
453
454impl PartialEq for Name<'_> {
455 fn eq(&self, other: &Self) -> bool {
456 self.labels == other.labels
457 }
458}
459
460impl Hash for Name<'_> {
461 fn hash<H: Hasher>(&self, state: &mut H) {
462 self.labels.hash(state);
463 }
464}
465
466#[cfg(test)]
467mod tests {
468 use super::*;
469 use crate::lib::Cursor;
470 use crate::{lib::Vec, SimpleDnsError};
471
472 #[test]
473 fn construct_valid_names() {
474 assert!(Name::new("some").is_ok());
475 assert!(Name::new("some.local").is_ok());
476 assert!(Name::new("some.local.").is_ok());
477 assert!(Name::new("some-dash.local.").is_ok());
478 assert!(Name::new("_sync_miss._tcp.local").is_ok());
479 assert!(Name::new("1sync_miss._tcp.local").is_ok());
480
481 assert_eq!(Name::new_unchecked("\u{1F600}.local.").labels.len(), 2);
482 }
483
484 #[test]
485 fn is_link_local() {
486 assert!(!Name::new("some.example.com").unwrap().is_link_local());
487 assert!(Name::new("some.example.local.").unwrap().is_link_local());
488 }
489
490 #[test]
491 fn parse_without_compression() {
492 let mut data = BytesBuffer::new(
493 b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\x01F\x03ISI\x04ARPA\x00\x04ARPA\x00",
494 );
495 data.advance(3).unwrap();
496 let name = Name::parse(&mut data).unwrap();
497 assert_eq!("F.ISI.ARPA", name.to_string());
498
499 let name = Name::parse(&mut data).unwrap();
500 assert_eq!("FOO.F.ISI.ARPA", name.to_string());
501 }
502
503 #[test]
504 fn parse_with_compression() {
505 let mut data = BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03\x07INVALID\xc0\x1b" );
506 data.advance(3).unwrap();
507
508 let name = Name::parse(&mut data).unwrap();
509 assert_eq!("F.ISI.ARPA", name.to_string());
510
511 let name = Name::parse(&mut data).unwrap();
512 assert_eq!("FOO.F.ISI.ARPA", name.to_string());
513
514 let name = Name::parse(&mut data).unwrap();
515 assert_eq!("BAR.F.ISI.ARPA", name.to_string());
516
517 assert!(Name::parse(&mut data).is_err());
518 }
519
520 #[test]
521 fn parse_handle_circular_pointers() {
522 let mut data = BytesBuffer::new(&[249, 0, 37, 1, 1, 139, 192, 6, 1, 1, 1, 139, 192, 6]);
523 data.advance(12).unwrap();
524
525 assert_eq!(
526 Name::parse(&mut data),
527 Err(SimpleDnsError::InvalidDnsPacket)
528 );
529 }
530
531 #[test]
532 fn test_write() {
533 let mut bytes = Vec::with_capacity(30);
534 Name::new_unchecked("_srv._udp.local")
535 .write_to(&mut bytes)
536 .unwrap();
537
538 assert_eq!(b"\x04_srv\x04_udp\x05local\x00", &bytes[..]);
539
540 let mut bytes = Vec::with_capacity(30);
541 Name::new_unchecked("_srv._udp.local2.")
542 .write_to(&mut bytes)
543 .unwrap();
544
545 assert_eq!(b"\x04_srv\x04_udp\x06local2\x00", &bytes[..]);
546 }
547
548 #[test]
549 fn root_name_should_generate_no_labels() {
550 assert_eq!(Name::new_unchecked("").labels.len(), 0);
551 assert_eq!(Name::new_unchecked(".").labels.len(), 0);
552 }
553
554 #[test]
555 fn dot_sequence_should_generate_no_labels() {
556 assert_eq!(Name::new_unchecked(".....").labels.len(), 0);
557 assert_eq!(Name::new_unchecked("example.....com").labels.len(), 2);
558 }
559
560 #[test]
561 fn root_name_should_write_zero() {
562 let mut bytes = Vec::with_capacity(30);
563 Name::new_unchecked(".").write_to(&mut bytes).unwrap();
564
565 assert_eq!(b"\x00", &bytes[..]);
566 }
567
568 #[test]
569 fn append_to_vec_with_compression() {
570 let mut buf = Cursor::new(crate::lib::vec![0, 0, 0]);
571 buf.set_position(3);
572
573 let mut name_refs = Default::default();
574
575 let f_isi_arpa = Name::new_unchecked("F.ISI.ARPA");
576 f_isi_arpa
577 .write_compressed_to(&mut buf, &mut name_refs)
578 .expect("failed to add F.ISI.ARPA");
579 let foo_f_isi_arpa = Name::new_unchecked("FOO.F.ISI.ARPA");
580 foo_f_isi_arpa
581 .write_compressed_to(&mut buf, &mut name_refs)
582 .expect("failed to add FOO.F.ISI.ARPA");
583
584 Name::new_unchecked("BAR.F.ISI.ARPA")
585 .write_compressed_to(&mut buf, &mut name_refs)
586 .expect("failed to add FOO.F.ISI.ARPA");
587
588 let data = b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03";
589 assert_eq!(data[..], buf.get_ref()[..]);
590 }
591
592 #[test]
593 fn append_to_vec_with_compression_mult_names() {
594 let mut buf = Cursor::new(Vec::new());
595 let mut name_refs = Default::default();
596
597 let isi_arpa = Name::new_unchecked("ISI.ARPA");
598 isi_arpa
599 .write_compressed_to(&mut buf, &mut name_refs)
600 .expect("failed to add ISI.ARPA");
601
602 let f_isi_arpa = Name::new_unchecked("F.ISI.ARPA");
603 f_isi_arpa
604 .write_compressed_to(&mut buf, &mut name_refs)
605 .expect("failed to add F.ISI.ARPA");
606 let foo_f_isi_arpa = Name::new_unchecked("FOO.F.ISI.ARPA");
607 foo_f_isi_arpa
608 .write_compressed_to(&mut buf, &mut name_refs)
609 .expect("failed to add F.ISI.ARPA");
610 Name::new_unchecked("BAR.F.ISI.ARPA")
611 .write_compressed_to(&mut buf, &mut name_refs)
612 .expect("failed to add F.ISI.ARPA");
613
614 let expected = b"\x03ISI\x04ARPA\x00\x01F\xc0\x00\x03FOO\xc0\x0a\x03BAR\xc0\x0a";
615 assert_eq!(expected[..], buf.get_ref()[..]);
616
617 let mut data = BytesBuffer::new(buf.get_ref());
618
619 let first = Name::parse(&mut data).unwrap();
620 assert_eq!("ISI.ARPA", first.to_string());
621 let second = Name::parse(&mut data).unwrap();
622 assert_eq!("F.ISI.ARPA", second.to_string());
623 let third = Name::parse(&mut data).unwrap();
624 assert_eq!("FOO.F.ISI.ARPA", third.to_string());
625 let fourth = Name::parse(&mut data).unwrap();
626 assert_eq!("BAR.F.ISI.ARPA", fourth.to_string());
627 }
628
629 #[test]
630 fn names_differing_only_in_case_are_not_compressed_together() {
631 let mut buf = Cursor::new(Vec::new());
632 let mut name_refs = Default::default();
633
634 let lower = Name::new_unchecked("foo.local");
635 let upper = Name::new_unchecked("FOO.LOCAL");
636 assert_eq!(lower, upper);
637
638 lower
639 .write_compressed_to(&mut buf, &mut name_refs)
640 .expect("failed to add foo.local");
641 upper
642 .write_compressed_to(&mut buf, &mut name_refs)
643 .expect("failed to add FOO.LOCAL");
644 upper
646 .write_compressed_to(&mut buf, &mut name_refs)
647 .expect("failed to add FOO.LOCAL again");
648
649 let expected = b"\x03foo\x05local\x00\x03FOO\x05LOCAL\x00\xc0\x0b";
650 assert_eq!(expected[..], buf.get_ref()[..]);
651
652 let mut data = BytesBuffer::new(buf.get_ref());
654 let first = Name::parse(&mut data).unwrap();
655 let second = Name::parse(&mut data).unwrap();
656 assert_eq!(b"FOO", second.get_labels()[0].as_ref());
657 assert_eq!(b"foo", first.get_labels()[0].as_ref());
658 }
659
660 #[test]
661 fn ensure_different_domains_are_not_compressed() {
662 let mut buf = Cursor::new(Vec::new());
663 let mut name_refs = Default::default();
664
665 let foo_bar_baz = Name::new_unchecked("FOO.BAR.BAZ");
666 foo_bar_baz
667 .write_compressed_to(&mut buf, &mut name_refs)
668 .expect("failed to add FOO.BAR.BAZ");
669
670 let foo_bar_buz = Name::new_unchecked("FOO.BAR.BUZ");
671 foo_bar_buz
672 .write_compressed_to(&mut buf, &mut name_refs)
673 .expect("failed to add FOO.BAR.BUZ");
674
675 Name::new_unchecked("FOO.BAR")
676 .write_compressed_to(&mut buf, &mut name_refs)
677 .expect("failed to add FOO.BAR");
678
679 let expected = b"\x03FOO\x03BAR\x03BAZ\x00\x03FOO\x03BAR\x03BUZ\x00\x03FOO\x03BAR\x00";
680 assert_eq!(expected[..], buf.get_ref()[..]);
681 }
682
683 #[test]
684 fn eq_other_name() -> Result<(), SimpleDnsError> {
685 assert_eq!(Name::new("example.com")?, Name::new("example.com")?);
686 assert_ne!(Name::new("some.example.com")?, Name::new("example.com")?);
687 assert_ne!(Name::new("example.co")?, Name::new("example.com")?);
688 assert_ne!(Name::new("example.com.org")?, Name::new("example.com")?);
689
690 let mut data =
691 BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03");
692 data.advance(3)?;
693 assert_eq!(Name::new("F.ISI.ARPA")?, Name::parse(&mut data)?);
694 assert_eq!(Name::new("FOO.F.ISI.ARPA")?, Name::parse(&mut data)?);
695 Ok(())
696 }
697
698 #[test]
699 fn len() -> crate::Result<()> {
700 let mut bytes = Vec::new();
701 let name_one = Name::new_unchecked("ex.com.");
702 name_one.write_to(&mut bytes)?;
703
704 assert_eq!(8, bytes.len());
705 assert_eq!(bytes.len(), name_one.len());
706 assert_eq!(8, Name::parse(&mut BytesBuffer::new(&bytes))?.len());
707
708 Ok(())
709 }
710
711 #[test]
712 fn len_compressed() -> crate::Result<()> {
713 let name_one = Name::new_unchecked("ex.com.");
714 let mut name_refs = Default::default();
715 let mut bytes = Cursor::new(Vec::new());
716 name_one.write_compressed_to(&mut bytes, &mut name_refs)?;
717 name_one.write_compressed_to(&mut bytes, &mut name_refs)?;
718
719 assert_eq!(10, bytes.get_ref().len());
720 Ok(())
721 }
722
723 #[test]
724 #[cfg(feature = "std")]
725 fn hash() -> crate::Result<()> {
726 fn get_hash(name: &Name) -> u64 {
727 let mut hasher = std::hash::DefaultHasher::default();
728 name.hash(&mut hasher);
729 hasher.finish()
730 }
731
732 let mut data =
733 BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03");
734 data.advance(3)?;
735
736 assert_eq!(
737 get_hash(&Name::new("F.ISI.ARPA")?),
738 get_hash(&Name::parse(&mut data)?)
739 );
740
741 assert_eq!(
742 get_hash(&Name::new("FOO.F.ISI.ARPA")?),
743 get_hash(&Name::parse(&mut data)?)
744 );
745
746 Ok(())
747 }
748
749 #[test]
750 fn is_subdomain_of() {
751 assert!(Name::new_unchecked("sub.example.com")
752 .is_subdomain_of(&Name::new_unchecked("example.com")));
753
754 assert!(!Name::new_unchecked("example.com")
755 .is_subdomain_of(&Name::new_unchecked("example.com")));
756
757 assert!(Name::new_unchecked("foo.sub.example.com")
758 .is_subdomain_of(&Name::new_unchecked("example.com")));
759
760 assert!(!Name::new_unchecked("example.com")
761 .is_subdomain_of(&Name::new_unchecked("example.xom")));
762
763 assert!(!Name::new_unchecked("domain.com")
764 .is_subdomain_of(&Name::new_unchecked("other.domain")));
765
766 assert!(!Name::new_unchecked("domain.com")
767 .is_subdomain_of(&Name::new_unchecked("domain.com.br")));
768 }
769
770 #[test]
771 fn subtract_domain() {
772 let domain = Name::new_unchecked("_srv3._tcp.local");
773 assert_eq!(
774 Name::new_unchecked("a._srv3._tcp.local")
775 .without(&domain)
776 .unwrap()
777 .to_string(),
778 "a"
779 );
780
781 assert!(Name::new_unchecked("unrelated").without(&domain).is_none(),);
782
783 assert_eq!(
784 Name::new_unchecked("some.longer.domain._srv3._tcp.local")
785 .without(&domain)
786 .unwrap()
787 .to_string(),
788 "some.longer.domain"
789 );
790 }
791
792 #[test]
793 fn test_compress_append_near_boundary() -> crate::Result<()> {
794 let mut buf = Cursor::new(Vec::new());
795 let mut name_refs = Default::default();
796
797 let before_boundary_pos = (MAX_COMPRESSION_OFFSET - 5) as usize;
798 let padding = vec![0u8; before_boundary_pos];
799 buf.write_all(&padding)?;
800
801 let name1 = Name::new_unchecked("foo.example.com");
802 let name2 = Name::new_unchecked("bar.test.net");
803
804 let old_pos = buf.position();
806 name1.write_compressed_to(&mut buf, &mut name_refs)?;
807 assert_eq!(buf.position() - old_pos, name1.len() as u64);
808
809 let old_pos = buf.position();
811 name2.write_compressed_to(&mut buf, &mut name_refs)?;
812 assert_eq!(buf.position() - old_pos, name2.len() as u64);
813
814 let old_pos = buf.position();
816 name1.write_compressed_to(&mut buf, &mut name_refs)?;
817 assert_eq!(buf.position() - old_pos, 2);
818
819 let old_pos = buf.position();
821 name2.write_compressed_to(&mut buf, &mut name_refs)?;
822 assert_eq!(buf.position() - old_pos, name2.len() as u64);
823
824 Ok(())
825 }
826
827 #[test]
828 fn case_insensitive_label_and_name() {
829 let lower = Name::new("_googlecast._tcp.local").unwrap();
830 let upper = Name::new("_googlecast._TCP.LOCAL").unwrap();
831
832 assert_eq!(lower, upper);
833 assert!(Name::new("device._googlecast._TCP.LOCAL")
834 .unwrap()
835 .is_subdomain_of(&lower));
836
837 let full = Name::new("device._googlecast._TCP.LOCAL").unwrap();
838 let sub = full.without(&lower).unwrap();
839 assert_eq!("device", sub.to_string());
840
841 let mut map = std::collections::HashMap::new();
842 map.insert(lower.clone(), 123);
843 assert_eq!(Some(&123), map.get(&upper));
844
845 let l1 = Label::new("Example".as_bytes()).unwrap();
846 let l2 = Label::new("example".as_bytes()).unwrap();
847 assert_eq!(l1, l2);
848 assert_eq!(core::cmp::Ordering::Equal, l1.cmp(&l2));
849 }
850
851 #[test]
852 fn escaped_dots_and_slashes() {
853 let name = Name::new(r"My\.Device._googlecast._tcp.local").expect("failed to build name");
854 assert_eq!(4, name.get_labels().len());
855 assert_eq!(b"My.Device", name.get_labels()[0].as_ref());
856 assert_eq!(r"My\.Device._googlecast._tcp.local", name.to_string());
857
858 let name2 = Name::new(r"My\.Device._googlecast._tcp.local").expect("failed to build name");
859 assert_eq!(4, name2.get_labels().len());
860 assert_eq!(b"My.Device", name2.get_labels()[0].as_ref());
861 assert_eq!(r"My\.Device._googlecast._tcp.local", name2.to_string());
862
863 let formatted = name2.to_string();
864 let roundtrip = Name::new(&formatted).expect("failed to build name");
865 assert_eq!(name2, roundtrip);
866
867 let name_slash = Name::new(r"My\\Device._tcp.local").expect("failed to build name");
868 assert_eq!(3, name_slash.get_labels().len());
869 assert_eq!(b"My\\Device", name_slash.get_labels()[0].as_ref());
870 assert_eq!(r"My\\Device._tcp.local", name_slash.to_string());
871 }
872
873 #[test]
874 fn escaped_dot_is_a_single_label_on_the_wire() {
875 let name = Name::new(r"a\.b.local").expect("failed to build name");
876 let mut bytes = Vec::new();
877 name.write_to(&mut bytes).unwrap();
878
879 assert_eq!(b"\x03a.b\x05local\x00", &bytes[..]);
881
882 let parsed = Name::parse(&mut BytesBuffer::new(&bytes)).unwrap();
883 assert_eq!(name, parsed);
884 assert_eq!(r"a\.b.local", parsed.to_string());
885 }
886
887 #[test]
888 fn backslash_is_escaped_in_display() {
889 let name = Name::new_with_labels(&[
890 Label::new_unchecked(b"a\\b".as_slice()),
891 Label::new_unchecked(b"local".as_slice()),
892 ]);
893 assert_eq!(r"a\\b.local", name.to_string());
894 }
895
896 #[test]
897 fn escaped_backslash_followed_by_separator_dot() {
898 let name = Name::new(r"a\\.local").expect("failed to build name");
900 assert_eq!(2, name.get_labels().len());
901 assert_eq!(b"a\\", name.get_labels()[0].as_ref());
902 assert_eq!(b"local", name.get_labels()[1].as_ref());
903 }
904
905 #[test]
906 fn escaped_dot_after_escaped_backslash() {
907 let name = Name::new(r"a\\\.b.local").expect("failed to build name");
909 assert_eq!(2, name.get_labels().len());
910 assert_eq!(b"a\\.b", name.get_labels()[0].as_ref());
911 }
912
913 #[test]
914 fn dns_sd_instance_name_roundtrip() {
915 let labels = [
917 Label::new_unchecked(b"My Printer. Floor 2".as_slice()),
918 Label::new_unchecked(b"_ipp".as_slice()),
919 Label::new_unchecked(b"_tcp".as_slice()),
920 Label::new_unchecked(b"local".as_slice()),
921 ];
922 let name = Name::new_with_labels(&labels);
923 let text = name.to_string();
924 assert_eq!(r"My Printer\. Floor 2._ipp._tcp.local", text);
925
926 let parsed = Name::new(&text).unwrap();
927 assert_eq!(name, parsed);
928 assert_eq!(4, parsed.get_labels().len());
929 }
930
931 #[test]
932 fn display_parse_roundtrip_with_special_bytes() {
933 let name = Name::new_with_labels(&[
934 Label::new_unchecked(b"a.b\\c.d".as_slice()),
935 Label::new_unchecked(b"local".as_slice()),
936 ]);
937 let text = name.to_string();
938 assert_eq!(r"a\.b\\c\.d.local", text);
939 assert_eq!(name, Name::new(&text).unwrap());
940 }
941
942 #[test]
943 fn decimal_escape_is_parsed_as_byte() {
944 let name = Name::new(r"My\032Printer.local").expect("failed to build name");
946 assert_eq!(2, name.get_labels().len());
947 assert_eq!(b"My Printer", name.get_labels()[0].as_ref());
948
949 let name = Name::new(r"a\046b.local").expect("failed to build name");
950 assert_eq!(2, name.get_labels().len());
951 assert_eq!(b"a.b", name.get_labels()[0].as_ref());
952 }
953
954 #[test]
955 fn escaped_non_special_char_is_the_char() {
956 let name =
958 Name::new(r"Stuart\'s\ Test\ Server._http._tcp.local").expect("failed to build name");
959 assert_eq!(4, name.get_labels().len());
960 assert_eq!(b"Stuart's Test Server", name.get_labels()[0].as_ref());
961 }
962
963 #[test]
964 fn label_length_limit_counts_unescaped_octets() {
965 let at_limit = format!("{}.local", r"a\.".repeat(31) + "a");
967 let name = Name::new(&at_limit).unwrap();
968 assert_eq!(63, name.get_labels()[0].len());
969
970 let over_limit = format!("{}.local", r"a\.".repeat(31) + "aa");
972 assert!(Name::new(&over_limit).is_err());
973 }
974
975 #[test]
976 fn malformed_decimal_escapes_are_rejected() {
977 assert!(Name::new(r"a\1.local").is_err());
979 assert!(Name::new(r"a\12.local").is_err());
980 assert!(Name::new(r"a\256b.local").is_err());
982 }
983
984 #[test]
985 fn trailing_lone_backslash_is_rejected() {
986 assert!(Name::new(r"local\").is_err());
987 assert!(Name::new(r"a.local\").is_err());
988 }
989
990 #[test]
991 fn decimal_escape_for_dot_is_data_not_separator() {
992 let name = Name::new(r"a\046b.local").expect("failed to build name");
994 assert_eq!(2, name.get_labels().len());
995 assert_eq!(b"a.b", name.get_labels()[0].as_ref());
996 assert_eq!(r"a\.b.local", name.to_string());
997 }
998
999 #[test]
1000 fn decimal_escape_followed_by_digit() {
1001 let name = Name::new(r"a\0321b.local").expect("failed to build name");
1003 assert_eq!(2, name.get_labels().len());
1004 assert_eq!(b"a 1b", name.get_labels()[0].as_ref());
1005 }
1006
1007 #[test]
1008 fn decimal_escape_boundaries() {
1009 let name = Name::new(r"a\000\255z.local").expect("failed to build name");
1010 assert_eq!(b"a\x00\xffz", name.get_labels()[0].as_ref());
1011
1012 let mut bytes = Vec::new();
1013 name.write_to(&mut bytes).unwrap();
1014 assert_eq!(b"\x04a\x00\xffz\x05local\x00", &bytes[..]);
1015 }
1016
1017 #[test]
1018 fn rfc4343_examples() {
1019 let name =
1020 Name::new(r"Donald\032E\.\032Eastlake\0323rd.example.").expect("failed to build name");
1021 assert_eq!(2, name.get_labels().len());
1022 assert_eq!(b"Donald E. Eastlake 3rd", name.get_labels()[0].as_ref());
1023
1024 let name = Name::new(r"a\000\\\255z.example.").expect("failed to build name");
1025 assert_eq!(2, name.get_labels().len());
1026 assert_eq!(b"a\x00\\\xffz", name.get_labels()[0].as_ref());
1027 }
1028
1029 #[test]
1030 fn escaped_dot_before_trailing_dot_is_not_a_separator() {
1031 let name = Name::new(r"a\.").expect("failed to build name");
1033 assert_eq!(1, name.get_labels().len());
1034 assert_eq!(b"a.", name.get_labels()[0].as_ref());
1035
1036 let absolute = Name::new(r"a\..").expect("failed to build name");
1037 assert_eq!(name, absolute);
1038 }
1039
1040 #[test]
1041 fn root_name_with_escapes_elsewhere() {
1042 assert_eq!(0, Name::new_unchecked(".").get_labels().len());
1043 assert_eq!(1, Name::new_unchecked(r"\.").get_labels().len());
1044 }
1045
1046 #[test]
1047 fn total_name_length_counts_unescaped_octets() {
1048 let label63 = r"a\.".repeat(31) + "a";
1049 let label60 = r"a\.".repeat(29) + "aa";
1050
1051 let fits = format!("{label63}.{label63}.{label63}.{label60}");
1053 let name = Name::new(&fits).unwrap();
1054 assert_eq!(254, name.len());
1055
1056 let too_long = format!("{label63}.{label63}.{label63}.{label63}");
1058 assert!(Name::new(&too_long).is_err());
1059 }
1060
1061 #[test]
1062 fn hostname_valid() {
1063 assert!(Name::new_unchecked("example.com").is_valid_hostname());
1064 assert!(Name::new_unchecked("example.com.").is_valid_hostname());
1065 assert!(Name::new_unchecked("my-host.sub.example.com").is_valid_hostname());
1066 assert!(Name::new_unchecked("1host.local").is_valid_hostname());
1067 assert!(Name::new_unchecked("localhost").is_valid_hostname());
1068 }
1069
1070 #[test]
1071 fn hostname_invalid() {
1072 assert!(!Name::new_unchecked("").is_valid_hostname());
1073 assert!(!Name::new_unchecked(".").is_valid_hostname());
1074 assert!(!Name::new_unchecked("-a.com").is_valid_hostname());
1075 assert!(!Name::new_unchecked("a-.com").is_valid_hostname());
1076 assert!(!Name::new_unchecked("_srv.local").is_valid_hostname());
1077 assert!(!Name::new_unchecked("a b.com").is_valid_hostname());
1078 assert!(!Name::new_unchecked("\u{1F600}.local").is_valid_hostname());
1079 assert!(!Name::new_unchecked(r"a\.b.com").is_valid_hostname());
1081 assert!(!Name::new_unchecked(&format!("{}.com", "a".repeat(64))).is_valid_hostname());
1083 }
1084
1085 #[test]
1086 fn service_name_valid() {
1087 assert!(Name::new_unchecked("_http._tcp.local").is_valid_service_name());
1089 assert!(Name::new_unchecked("_http._udp.local").is_valid_service_name());
1090 assert!(Name::new_unchecked("_http._tcp.local.").is_valid_service_name());
1091 assert!(Name::new_unchecked("_ipp-tls._tcp.example.com").is_valid_service_name());
1092 assert!(Name::new_unchecked("_9p._tcp.local").is_valid_service_name());
1093 }
1094
1095 #[test]
1096 fn service_name_transport_is_case_insensitive() {
1097 assert!(Name::new_unchecked("_HTTP._TCP.local").is_valid_service_name());
1099 assert!(Name::new_unchecked("_http._Udp.local").is_valid_service_name());
1100 }
1101
1102 #[test]
1103 fn service_name_invalid() {
1104 assert!(!Name::new_unchecked("").is_valid_service_name());
1105 assert!(!Name::new_unchecked("http._tcp.local").is_valid_service_name());
1107 assert!(!Name::new_unchecked("_http._ftp.local").is_valid_service_name());
1109 assert!(!Name::new_unchecked("_http.tcp.local").is_valid_service_name());
1110 assert!(!Name::new_unchecked("_http.local").is_valid_service_name());
1111 assert!(!Name::new_unchecked("_http").is_valid_service_name());
1113 assert!(!Name::new_unchecked("_123._tcp.local").is_valid_service_name());
1115 assert!(!Name::new_unchecked("_a--b._tcp.local").is_valid_service_name());
1116 assert!(!Name::new_unchecked("_-a._tcp.local").is_valid_service_name());
1117 assert!(!Name::new_unchecked("_abcdefghijklmnop._tcp.local").is_valid_service_name());
1118 assert!(!Name::new_unchecked("_http._tcp.-bad.local").is_valid_service_name());
1120 }
1121
1122 #[test]
1123 fn instance_name_valid() {
1124 assert!(Name::new_unchecked("printer._ipp._tcp.local").is_valid_instance_name());
1126 assert!(Name::new_unchecked("My Printer._ipp._tcp.local").is_valid_instance_name());
1127 assert!(Name::new_unchecked("My Printer._ipp._tcp.local.").is_valid_instance_name());
1128 assert!(
1129 Name::new_unchecked("Stuart's Test Server._http._tcp.local").is_valid_instance_name()
1130 );
1131 assert!(Name::new_unchecked("\u{1F5A8} Printer._ipp._udp.local").is_valid_instance_name());
1132 assert!(Name::new_unchecked(r"My\. Printer._ipp._tcp.local").is_valid_instance_name());
1134 assert!(Name::new_with_labels(&[
1135 Label::new_unchecked(b"My. Printer".as_slice()),
1136 Label::new_unchecked(b"_ipp".as_slice()),
1137 Label::new_unchecked(b"_tcp".as_slice()),
1138 Label::new_unchecked(b"local".as_slice()),
1139 ])
1140 .is_valid_instance_name());
1141 }
1142
1143 #[test]
1144 fn instance_name_escaped_dot_keeps_label_count() {
1145 let name = Name::new_unchecked(r"a\.b._ipp._tcp.local");
1146 assert_eq!(4, name.get_labels().len());
1147 assert!(name.is_valid_instance_name());
1148
1149 let name = Name::new_unchecked("a.b._ipp._tcp.local");
1151 assert_eq!(5, name.get_labels().len());
1152 assert!(!name.is_valid_instance_name());
1153 }
1154
1155 #[test]
1156 fn instance_name_invalid() {
1157 assert!(!Name::new_unchecked("").is_valid_instance_name());
1158 assert!(!Name::new_unchecked("_ipp._tcp.local").is_valid_instance_name());
1160 assert!(!Name::new_unchecked("My Printer._ipp._ftp.local").is_valid_instance_name());
1161 assert!(!Name::new_unchecked("My Printer.ipp._tcp.local").is_valid_instance_name());
1162 assert!(!Name::new_unchecked("My Printer._123._tcp.local").is_valid_instance_name());
1163 assert!(!Name::new_unchecked("My Printer").is_valid_instance_name());
1165 assert!(!Name::new_unchecked("My Printer._ipp").is_valid_instance_name());
1166 let long = format!("{}._ipp._tcp.local", "a".repeat(64));
1168 assert!(!Name::new_unchecked(&long).is_valid_instance_name());
1169 }
1170
1171 #[test]
1172 fn sub_type_name_valid() {
1173 assert!(Name::new_unchecked("_printer._sub._http._tcp.local").is_valid_sub_type_name());
1175 assert!(Name::new_unchecked("_printer._sub._http._udp.local.").is_valid_sub_type_name());
1176 }
1177
1178 #[test]
1179 fn sub_type_name_allows_arbitrary_label() {
1180 assert!(Name::new_unchecked("printer._sub._http._tcp.local").is_valid_sub_type_name());
1183 assert!(
1184 Name::new_unchecked("The Music Store._sub._http._tcp.local").is_valid_sub_type_name()
1185 );
1186 assert!(Name::new_with_labels(&[
1187 Label::new_unchecked(b"a.b\xff".as_slice()),
1188 Label::new_unchecked(b"_sub".as_slice()),
1189 Label::new_unchecked(b"_http".as_slice()),
1190 Label::new_unchecked(b"_tcp".as_slice()),
1191 Label::new_unchecked(b"local".as_slice()),
1192 ])
1193 .is_valid_sub_type_name());
1194 }
1195
1196 #[test]
1197 fn sub_type_name_invalid() {
1198 assert!(!Name::new_unchecked("").is_valid_sub_type_name());
1199 assert!(!Name::new_unchecked("_printer._http._tcp.local").is_valid_sub_type_name());
1201 assert!(!Name::new_unchecked("_printer._subx._http._tcp.local").is_valid_sub_type_name());
1202 assert!(!Name::new_unchecked("_printer._sub.http._tcp.local").is_valid_sub_type_name());
1204 assert!(!Name::new_unchecked("_printer._sub._http._ftp.local").is_valid_sub_type_name());
1205 assert!(!Name::new_unchecked("_printer._sub._123._tcp.local").is_valid_sub_type_name());
1206 assert!(!Name::new_unchecked("_printer._sub").is_valid_sub_type_name());
1208 assert!(!Name::new_unchecked("_printer").is_valid_sub_type_name());
1209 }
1210}