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 serde::{Deserialize, Serialize};
25use servo_url::{Host, ImmutableOrigin, ServoUrl};
26
27#[derive(Clone, Debug, Default, MallocSizeOf)]
30pub struct PubDomainRules {
31 rules: FxHashSet<String>,
32 wildcards: FxHashSet<String>,
33 exceptions: FxHashSet<String>,
34}
35
36static PUB_DOMAINS: LazyLock<PubDomainRules> = LazyLock::new(load_pub_domains);
37
38pub fn public_suffix_list_size_of(ops: &mut MallocSizeOfOps) -> usize {
39 PUB_DOMAINS.size_of(ops)
40}
41
42impl<'a> FromIterator<&'a str> for PubDomainRules {
43 fn from_iter<T>(iter: T) -> Self
44 where
45 T: IntoIterator<Item = &'a str>,
46 {
47 let mut result = PubDomainRules::default();
48 for item in iter {
49 if let Some(stripped) = item.strip_prefix('!') {
50 result.exceptions.insert(String::from(stripped));
51 } else if let Some(stripped) = item.strip_prefix("*.") {
52 result.wildcards.insert(String::from(stripped));
53 } else {
54 result.rules.insert(String::from(item));
55 }
56 }
57 result
58 }
59}
60
61impl PubDomainRules {
62 pub fn parse(content: &str) -> PubDomainRules {
63 content
64 .lines()
65 .map(str::trim)
66 .filter(|s| !s.is_empty())
67 .filter(|s| !s.starts_with("//"))
68 .collect()
69 }
70 fn suffix_pair<'a>(&self, domain: &'a str) -> (&'a str, &'a str) {
71 let domain = domain.trim_start_matches('.');
72 let mut suffix = domain;
73 let mut prev_suffix = domain;
74 for (index, _) in domain.match_indices('.') {
75 let next_suffix = &domain[index + 1..];
76 if self.exceptions.contains(suffix) {
77 return (next_suffix, suffix);
78 }
79 if self.wildcards.contains(next_suffix) || self.rules.contains(suffix) {
80 return (suffix, prev_suffix);
81 }
82 prev_suffix = suffix;
83 suffix = next_suffix;
84 }
85 (suffix, prev_suffix)
86 }
87 pub fn public_suffix<'a>(&self, domain: &'a str) -> &'a str {
88 let (public, _) = self.suffix_pair(domain);
89 public
90 }
91 pub fn registrable_suffix<'a>(&self, domain: &'a str) -> &'a str {
92 let (_, registrable) = self.suffix_pair(domain);
93 registrable
94 }
95 pub fn is_public_suffix(&self, domain: &str) -> bool {
96 let domain = domain.trim_start_matches('.');
100 match domain.find('.') {
101 None => !domain.is_empty(),
102 Some(index) => {
103 !self.exceptions.contains(domain) && self.wildcards.contains(&domain[index + 1..]) ||
104 self.rules.contains(domain)
105 },
106 }
107 }
108 pub fn is_registrable_suffix(&self, domain: &str) -> bool {
109 let domain = domain.trim_start_matches('.');
113 match domain.find('.') {
114 None => false,
115 Some(index) => {
116 self.exceptions.contains(domain) ||
117 !self.wildcards.contains(&domain[index + 1..]) &&
118 !self.rules.contains(domain) &&
119 self.is_public_suffix(&domain[index + 1..])
120 },
121 }
122 }
123}
124
125fn load_pub_domains() -> PubDomainRules {
126 PubDomainRules::parse(&resources::read_string(Resource::DomainList))
127}
128
129pub fn pub_suffix(domain: &str) -> &str {
130 PUB_DOMAINS.public_suffix(domain)
131}
132
133pub fn reg_suffix(domain: &str) -> &str {
134 PUB_DOMAINS.registrable_suffix(domain)
135}
136
137pub fn is_pub_domain(domain: &str) -> bool {
138 PUB_DOMAINS.is_public_suffix(domain)
139}
140
141pub fn is_reg_domain(domain: &str) -> bool {
142 PUB_DOMAINS.is_registrable_suffix(domain)
143}
144
145pub fn is_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
147 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
152 return true;
153 }
154
155 let ImmutableOrigin::Tuple(scheme_a, host_a, _) = site_a else {
157 return false;
158 };
159 let ImmutableOrigin::Tuple(scheme_b, host_b, _) = site_b else {
160 return false;
161 };
162
163 if scheme_a != scheme_b {
165 return false;
166 }
167
168 if let (Host::Domain(domain_a), Host::Domain(domain_b)) = (host_a, host_b) {
171 if reg_suffix(domain_a) != reg_suffix(domain_b) {
172 return false;
173 }
174 } else if host_a != host_b {
175 return false;
176 }
177
178 true
180}
181
182pub fn registered_domain_name(url: &ServoUrl) -> Option<Host> {
187 match url.origin() {
188 ImmutableOrigin::Tuple(_, Host::Domain(domain), _) => {
189 Some(Host::Domain(String::from(reg_suffix(&domain))))
190 },
191 ImmutableOrigin::Tuple(_, ip, _) => Some(ip),
192 ImmutableOrigin::Opaque(_) => None,
193 }
194}
195
196#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
198pub enum Site {
199 Opaque(ImmutableOrigin),
200 Tuple { scheme: String, host: Host },
201}
202
203impl Site {
204 pub fn from_origin(origin: &ImmutableOrigin) -> Self {
206 match origin {
207 ImmutableOrigin::Opaque(_) => Site::Opaque(origin.clone()),
209 ImmutableOrigin::Tuple(scheme, Host::Domain(domain), _) => Site::Tuple {
211 scheme: scheme.clone(),
212 host: Host::Domain(reg_suffix(domain).to_owned()),
213 },
214 ImmutableOrigin::Tuple(scheme, host, _) => Site::Tuple {
216 scheme: scheme.clone(),
217 host: host.clone(),
218 },
219 }
220 }
221}