Skip to main content

addr/
dns.rs

1//! DNS types
2
3use crate::error::Result;
4use crate::matcher;
5use core::{fmt, str};
6use psl_types::{List, Suffix, Type};
7
8/// Holds information about a particular DNS name
9#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
10pub struct Name<'a> {
11    full: &'a str,
12    suffix: Option<Suffix<'a>>,
13}
14
15impl<'a> Name<'a> {
16    pub(crate) fn parse<T: List + ?Sized>(list: &T, name: &'a str) -> Result<Name<'a>> {
17        matcher::is_dns_name(name)?;
18        Ok(Self {
19            full: name,
20            suffix: list.suffix(name.as_bytes()),
21        })
22    }
23
24    /// Full dns name as a `str`
25    pub const fn as_str(&self) -> &'a str {
26        self.full
27    }
28
29    fn without_suffix(&self) -> Option<&'a str> {
30        let domain_len = self.full.len();
31        let suffix_len = self.suffix()?.len();
32        if domain_len == suffix_len {
33            return None;
34        }
35        self.full.get(..domain_len - suffix_len - 1)
36    }
37
38    /// The root domain (the registrable part)
39    pub fn root(&self) -> Option<&'a str> {
40        let offset = self
41            .without_suffix()?
42            .rfind('.')
43            .map(|x| x + 1)
44            .unwrap_or_default();
45        self.full.get(offset..)
46    }
47
48    /// The part before the root domain (aka. subdomain)
49    pub fn prefix(&self) -> Option<&'a str> {
50        let domain_len = self.full.len();
51        let root_len = self.root()?.len();
52        if domain_len == root_len {
53            return None;
54        }
55        self.full.get(..domain_len - root_len - 1)
56    }
57
58    /// The domain name suffix (extension)
59    pub fn suffix(&self) -> Option<&'a str> {
60        let bytes = self.suffix.as_ref()?.as_bytes();
61        str::from_utf8(bytes).ok()
62    }
63
64    /// Whether the suffix of the domain name is in the Public Suffix List
65    pub fn has_known_suffix(&self) -> bool {
66        if let Some(suffix) = self.suffix {
67            suffix.is_known()
68        } else {
69            false
70        }
71    }
72
73    /// Whether this an ICANN delegated suffix
74    ///
75    /// ICANN domains are those delegated by ICANN or part of the IANA root
76    /// zone database
77    pub fn is_icann(&self) -> bool {
78        if let Some(suffix) = self.suffix {
79            suffix.typ() == Some(Type::Icann)
80        } else {
81            false
82        }
83    }
84
85    /// Whether this is a private party delegated suffix
86    ///
87    /// PRIVATE domains are amendments submitted by the domain holder, as an
88    /// expression of how they operate their domain security policy
89    pub fn is_private(&self) -> bool {
90        if let Some(suffix) = self.suffix {
91            suffix.typ() == Some(Type::Private)
92        } else {
93            false
94        }
95    }
96}
97
98impl fmt::Display for Name<'_> {
99    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100        write!(f, "{}", self.full)
101    }
102}
103
104impl PartialEq<&str> for Name<'_> {
105    fn eq(&self, other: &&str) -> bool {
106        self.full == *other
107    }
108}