Skip to main content

addr/
domain.rs

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