dns_message_parser/
domain_name.rs

1use crate::{Label, LabelError};
2use std::{
3    fmt::{Display, Formatter, Result as FmtResult},
4    hash::Hash,
5    str::FromStr,
6};
7use thiserror::Error;
8
9pub const DOMAIN_NAME_MAX_RECURSION: usize = 16;
10pub const DOMAIN_NAME_MAX_LENGTH: usize = 256;
11
12#[derive(Debug, PartialEq, Eq, Error)]
13pub enum DomainNameError {
14    #[error("Domain name is too big: {DOMAIN_NAME_MAX_LENGTH} <= {0}")]
15    DomainNameLength(usize),
16    #[error("{0}")]
17    LabelError(#[from] LabelError),
18}
19
20/// Represent a domain name according to [RFC 2181](https://datatracker.ietf.org/doc/html/rfc2181#section-11).
21#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
22pub struct DomainName(pub(super) Vec<Label>);
23
24impl DomainName {
25    /// Append a label to the domain name.
26    ///
27    /// If the label cannot be appended then the domain name is not changed.
28    /// The label cannot be appended if the domain name would be too big.
29    ///
30    /// # Example
31    /// ```
32    /// # use dns_message_parser::DomainName;
33    /// let mut domain_name = DomainName::default();
34    /// // Prints "."
35    /// println!("{}", domain_name);
36    ///
37    /// domain_name.append_label("example".parse().unwrap()).unwrap();
38    /// // Prints "example."
39    /// println!("{}", domain_name);
40    ///
41    /// domain_name.append_label("org".parse().unwrap()).unwrap();
42    /// // Prints "example.org."
43    /// println!("{}", domain_name);
44    /// ```
45    pub fn append_label(&mut self, label: Label) -> Result<(), DomainNameError> {
46        let label_length = label.len();
47        let domain_name_length = if self.0.is_empty() {
48            label_length + 1
49        } else {
50            self.len() + label_length + 1
51        };
52
53        if DOMAIN_NAME_MAX_LENGTH <= domain_name_length {
54            return Err(DomainNameError::DomainNameLength(domain_name_length));
55        }
56
57        self.0.push(label);
58        Ok(())
59    }
60
61    pub fn len(&self) -> usize {
62        let labels = self.0.len();
63        if labels == 0 {
64            1
65        } else {
66            let mut length = labels;
67            for label in self.0.iter() {
68                length += label.len();
69            }
70            length
71        }
72    }
73
74    #[inline]
75    pub fn is_root(&self) -> bool {
76        self.0.is_empty()
77    }
78}
79
80impl FromStr for DomainName {
81    type Err = DomainNameError;
82
83    fn from_str(string: &str) -> Result<Self, <Self as FromStr>::Err> {
84        let string_relativ = if let Some(string_relativ) = string.strip_suffix('.') {
85            string_relativ
86        } else {
87            string
88        };
89
90        let mut domain_name = DomainName::default();
91        for label in string_relativ.split('.') {
92            let label = label.parse()?;
93            domain_name.append_label(label)?;
94        }
95        Ok(domain_name)
96    }
97}
98
99impl Display for DomainName {
100    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
101        if self.is_root() {
102            write!(f, ".")
103        } else {
104            for label in self.0.iter() {
105                write!(f, "{}.", label)?;
106            }
107            Ok(())
108        }
109    }
110}