net_traits/
pub_domains.rs1use std::iter::FromIterator;
18use std::sync::LazyLock;
19
20use embedder_traits::resources::{self, Resource};
21use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
22use malloc_size_of_derive::MallocSizeOf;
23use rustc_hash::FxHashSet;
24use servo_url::{Host, ImmutableOrigin, ServoUrl};
25
26#[derive(Clone, Debug, Default, MallocSizeOf)]
29pub struct PubDomainRules {
30 rules: FxHashSet<String>,
31 wildcards: FxHashSet<String>,
32 exceptions: FxHashSet<String>,
33}
34
35static PUB_DOMAINS: LazyLock<PubDomainRules> = LazyLock::new(load_pub_domains);
36
37pub fn public_suffix_list_size_of(ops: &mut MallocSizeOfOps) -> usize {
38 PUB_DOMAINS.size_of(ops)
39}
40
41impl<'a> FromIterator<&'a str> for PubDomainRules {
42 fn from_iter<T>(iter: T) -> Self
43 where
44 T: IntoIterator<Item = &'a str>,
45 {
46 let mut result = PubDomainRules::default();
47 for item in iter {
48 if let Some(stripped) = item.strip_prefix('!') {
49 result.exceptions.insert(String::from(stripped));
50 } else if let Some(stripped) = item.strip_prefix("*.") {
51 result.wildcards.insert(String::from(stripped));
52 } else {
53 result.rules.insert(String::from(item));
54 }
55 }
56 result
57 }
58}
59
60impl PubDomainRules {
61 pub fn parse(content: &str) -> PubDomainRules {
62 content
63 .lines()
64 .map(str::trim)
65 .filter(|s| !s.is_empty())
66 .filter(|s| !s.starts_with("//"))
67 .collect()
68 }
69 fn suffix_pair<'a>(&self, domain: &'a str) -> (&'a str, &'a str) {
70 let domain = domain.trim_start_matches('.');
71 let mut suffix = domain;
72 let mut prev_suffix = domain;
73 for (index, _) in domain.match_indices('.') {
74 let next_suffix = &domain[index + 1..];
75 if self.exceptions.contains(suffix) {
76 return (next_suffix, suffix);
77 }
78 if self.wildcards.contains(next_suffix) || self.rules.contains(suffix) {
79 return (suffix, prev_suffix);
80 }
81 prev_suffix = suffix;
82 suffix = next_suffix;
83 }
84 (suffix, prev_suffix)
85 }
86 pub fn public_suffix<'a>(&self, domain: &'a str) -> &'a str {
87 let (public, _) = self.suffix_pair(domain);
88 public
89 }
90 pub fn registrable_suffix<'a>(&self, domain: &'a str) -> &'a str {
91 let (_, registrable) = self.suffix_pair(domain);
92 registrable
93 }
94 pub fn is_public_suffix(&self, domain: &str) -> bool {
95 let domain = domain.trim_start_matches('.');
99 match domain.find('.') {
100 None => !domain.is_empty(),
101 Some(index) => {
102 !self.exceptions.contains(domain) && self.wildcards.contains(&domain[index + 1..]) ||
103 self.rules.contains(domain)
104 },
105 }
106 }
107 pub fn is_registrable_suffix(&self, domain: &str) -> bool {
108 let domain = domain.trim_start_matches('.');
112 match domain.find('.') {
113 None => false,
114 Some(index) => {
115 self.exceptions.contains(domain) ||
116 !self.wildcards.contains(&domain[index + 1..]) &&
117 !self.rules.contains(domain) &&
118 self.is_public_suffix(&domain[index + 1..])
119 },
120 }
121 }
122}
123
124fn load_pub_domains() -> PubDomainRules {
125 PubDomainRules::parse(&resources::read_string(Resource::DomainList))
126}
127
128pub fn pub_suffix(domain: &str) -> &str {
129 PUB_DOMAINS.public_suffix(domain)
130}
131
132pub fn reg_suffix(domain: &str) -> &str {
133 PUB_DOMAINS.registrable_suffix(domain)
134}
135
136pub fn is_pub_domain(domain: &str) -> bool {
137 PUB_DOMAINS.is_public_suffix(domain)
138}
139
140pub fn is_reg_domain(domain: &str) -> bool {
141 PUB_DOMAINS.is_registrable_suffix(domain)
142}
143
144pub fn reg_host(url: &ServoUrl) -> Option<Host> {
149 match url.origin() {
150 ImmutableOrigin::Tuple(_, Host::Domain(domain), _) => {
151 Some(Host::Domain(String::from(reg_suffix(&domain))))
152 },
153 ImmutableOrigin::Tuple(_, ip, _) => Some(ip),
154 ImmutableOrigin::Opaque(_) => None,
155 }
156}