Commit
+79 -49 +/-6 browse
1 | diff --git a/README.md b/README.md |
2 | index 4cd2f5b..963779c 100644 |
3 | --- a/README.md |
4 | +++ b/README.md |
5 | @@ -140,13 +140,13 @@ Features: |
6 | |
7 | // Verify HELO identity |
8 | let result = resolver |
9 | - .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com") |
10 | + .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com", "my-local-domain.org") |
11 | .await; |
12 | assert_eq!(result.result(), SpfResult::Fail); |
13 | |
14 | // Verify MAIL-FROM identity |
15 | let result = resolver |
16 | - .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "sender@gmail.com") |
17 | + .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "my-local-domain.org", "sender@gmail.com") |
18 | .await; |
19 | assert_eq!(result.result(), SpfResult::Fail); |
20 | ``` |
21 | @@ -163,7 +163,7 @@ Features: |
22 | |
23 | // Verify SPF MAIL-FROM identity |
24 | let spf_result = resolver |
25 | - .verify_spf_sender("::1".parse().unwrap(), "example.org", "sender@example.org") |
26 | + .verify_spf_sender("::1".parse().unwrap(), "example.org", "my-local-domain.org", "sender@example.org") |
27 | .await; |
28 | |
29 | // Verify DMARC |
30 | diff --git a/examples/dmarc_verify.rs b/examples/dmarc_verify.rs |
31 | index b43a7ec..55f5f53 100644 |
32 | --- a/examples/dmarc_verify.rs |
33 | +++ b/examples/dmarc_verify.rs |
34 | @@ -48,7 +48,12 @@ async fn main() { |
35 | |
36 | // Verify SPF MAIL-FROM identity |
37 | let spf_result = resolver |
38 | - .verify_spf_sender("::1".parse().unwrap(), "example.org", "sender@example.org") |
39 | + .verify_spf_sender( |
40 | + "::1".parse().unwrap(), |
41 | + "example.org", |
42 | + "my-host-domain.org", |
43 | + "sender@example.org", |
44 | + ) |
45 | .await; |
46 | |
47 | // Verify DMARC |
48 | diff --git a/examples/spf_verify.rs b/examples/spf_verify.rs |
49 | index f960b52..7b1b666 100644 |
50 | --- a/examples/spf_verify.rs |
51 | +++ b/examples/spf_verify.rs |
52 | @@ -17,13 +17,22 @@ async fn main() { |
53 | |
54 | // Verify HELO identity |
55 | let result = resolver |
56 | - .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com") |
57 | + .verify_spf_helo( |
58 | + "127.0.0.1".parse().unwrap(), |
59 | + "gmail.com", |
60 | + "my-host-domain.org", |
61 | + ) |
62 | .await; |
63 | assert_eq!(result.result(), SpfResult::Fail); |
64 | |
65 | // Verify MAIL-FROM identity |
66 | let result = resolver |
67 | - .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "sender@gmail.com") |
68 | + .verify_spf_sender( |
69 | + "::1".parse().unwrap(), |
70 | + "gmail.com", |
71 | + "my-host-domain.org", |
72 | + "sender@gmail.com", |
73 | + ) |
74 | .await; |
75 | assert_eq!(result.result(), SpfResult::Fail); |
76 | } |
77 | diff --git a/src/common/resolver.rs b/src/common/resolver.rs |
78 | index 55c5506..b9e407a 100644 |
79 | --- a/src/common/resolver.rs |
80 | +++ b/src/common/resolver.rs |
81 | @@ -26,7 +26,7 @@ use crate::{ |
82 | dkim::{Atps, DomainKeyReport}, |
83 | dmarc::Dmarc, |
84 | spf::{Macro, Spf}, |
85 | - Error, Policy, Resolver, Txt, MX, |
86 | + Error, Resolver, Txt, MX, |
87 | }; |
88 | |
89 | use super::{ |
90 | @@ -37,7 +37,7 @@ use super::{ |
91 | |
92 | impl Resolver { |
93 | pub fn new_cloudflare_tls() -> Result<Self, ResolveError> { |
94 | - Self::new( |
95 | + Self::with_capacity( |
96 | ResolverConfig::cloudflare_tls(), |
97 | ResolverOpts::default(), |
98 | 128, |
99 | @@ -45,27 +45,27 @@ impl Resolver { |
100 | } |
101 | |
102 | pub fn new_cloudflare() -> Result<Self, ResolveError> { |
103 | - Self::new(ResolverConfig::cloudflare(), ResolverOpts::default(), 128) |
104 | + Self::with_capacity(ResolverConfig::cloudflare(), ResolverOpts::default(), 128) |
105 | } |
106 | |
107 | pub fn new_google() -> Result<Self, ResolveError> { |
108 | - Self::new(ResolverConfig::google(), ResolverOpts::default(), 128) |
109 | + Self::with_capacity(ResolverConfig::google(), ResolverOpts::default(), 128) |
110 | } |
111 | |
112 | pub fn new_quad9() -> Result<Self, ResolveError> { |
113 | - Self::new(ResolverConfig::quad9(), ResolverOpts::default(), 128) |
114 | + Self::with_capacity(ResolverConfig::quad9(), ResolverOpts::default(), 128) |
115 | } |
116 | |
117 | pub fn new_quad9_tls() -> Result<Self, ResolveError> { |
118 | - Self::new(ResolverConfig::quad9_tls(), ResolverOpts::default(), 128) |
119 | + Self::with_capacity(ResolverConfig::quad9_tls(), ResolverOpts::default(), 128) |
120 | } |
121 | |
122 | pub fn new_system_conf() -> Result<Self, ResolveError> { |
123 | let (config, options) = read_system_conf()?; |
124 | - Self::new(config, options, 128) |
125 | + Self::with_capacity(config, options, 128) |
126 | } |
127 | |
128 | - pub fn new( |
129 | + pub fn with_capacity( |
130 | config: ResolverConfig, |
131 | options: ResolverOpts, |
132 | capacity: usize, |
133 | @@ -77,19 +77,26 @@ impl Resolver { |
134 | cache_ipv4: LruCache::with_capacity(capacity), |
135 | cache_ipv6: LruCache::with_capacity(capacity), |
136 | cache_ptr: LruCache::with_capacity(capacity), |
137 | - host_domain: String::new(), |
138 | - verify_policy: Policy::VeryStrict, |
139 | }) |
140 | } |
141 | |
142 | - pub fn with_host_domain(mut self, hostname: &str) -> Self { |
143 | - self.host_domain = hostname.to_lowercase(); |
144 | - self |
145 | - } |
146 | - |
147 | - pub fn with_policy(mut self, policy: Policy) -> Self { |
148 | - self.verify_policy = policy; |
149 | - self |
150 | + pub fn with_capacities( |
151 | + config: ResolverConfig, |
152 | + options: ResolverOpts, |
153 | + txt_capacity: usize, |
154 | + mx_capacity: usize, |
155 | + ipv4_capacity: usize, |
156 | + ipv6_capacity: usize, |
157 | + ptr_capacity: usize, |
158 | + ) -> Result<Self, ResolveError> { |
159 | + Ok(Self { |
160 | + resolver: AsyncResolver::tokio(config, options)?, |
161 | + cache_txt: LruCache::with_capacity(txt_capacity), |
162 | + cache_mx: LruCache::with_capacity(mx_capacity), |
163 | + cache_ipv4: LruCache::with_capacity(ipv4_capacity), |
164 | + cache_ipv6: LruCache::with_capacity(ipv6_capacity), |
165 | + cache_ptr: LruCache::with_capacity(ptr_capacity), |
166 | + }) |
167 | } |
168 | |
169 | pub(crate) async fn txt_lookup<'x, T: TxtRecordParser + Into<Txt> + UnwrapTxtRecord>( |
170 | diff --git a/src/lib.rs b/src/lib.rs |
171 | index 52fc139..791a721 100644 |
172 | --- a/src/lib.rs |
173 | +++ b/src/lib.rs |
174 | @@ -150,13 +150,13 @@ |
175 | //! |
176 | //! // Verify HELO identity |
177 | //! let result = resolver |
178 | - //! .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com") |
179 | + //! .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com", "my-local-domain.org") |
180 | //! .await; |
181 | //! assert_eq!(result.result(), SpfResult::Fail); |
182 | //! |
183 | //! // Verify MAIL-FROM identity |
184 | //! let result = resolver |
185 | - //! .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "sender@gmail.com") |
186 | + //! .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "my-local-domain.org", "sender@gmail.com") |
187 | //! .await; |
188 | //! assert_eq!(result.result(), SpfResult::Fail); |
189 | //! ``` |
190 | @@ -173,7 +173,7 @@ |
191 | //! |
192 | //! // Verify SPF MAIL-FROM identity |
193 | //! let spf_result = resolver |
194 | - //! .verify_spf_sender("::1".parse().unwrap(), "example.org", "sender@example.org") |
195 | + //! .verify_spf_sender("::1".parse().unwrap(), "example.org", "my-local-domain.org", "sender@example.org") |
196 | //! .await; |
197 | //! |
198 | //! // Verify DMARC |
199 | @@ -275,6 +275,8 @@ pub mod dmarc; |
200 | pub mod report; |
201 | pub mod spf; |
202 | |
203 | + pub use trust_dns_resolver; |
204 | + |
205 | pub struct Resolver { |
206 | pub(crate) resolver: TokioAsyncResolver, |
207 | pub(crate) cache_txt: LruCache<String, Txt>, |
208 | @@ -282,8 +284,6 @@ pub struct Resolver { |
209 | pub(crate) cache_ipv4: LruCache<String, Arc<Vec<Ipv4Addr>>>, |
210 | pub(crate) cache_ipv6: LruCache<String, Arc<Vec<Ipv6Addr>>>, |
211 | pub(crate) cache_ptr: LruCache<IpAddr, Arc<Vec<String>>>, |
212 | - pub(crate) host_domain: String, |
213 | - pub(crate) verify_policy: Policy, |
214 | } |
215 | |
216 | #[derive(Clone)] |
217 | @@ -303,13 +303,6 @@ pub struct MX { |
218 | preference: u16, |
219 | } |
220 | |
221 | - #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
222 | - pub enum Policy { |
223 | - Relaxed, |
224 | - Strict, |
225 | - VeryStrict, |
226 | - } |
227 | - |
228 | #[derive(Debug, Clone)] |
229 | pub struct AuthenticatedMessage<'x> { |
230 | pub(crate) headers: Vec<(&'x [u8], &'x [u8])>, |
231 | diff --git a/src/spf/verify.rs b/src/spf/verify.rs |
232 | index 7655943..6da76ed 100644 |
233 | --- a/src/spf/verify.rs |
234 | +++ b/src/spf/verify.rs |
235 | @@ -13,18 +13,24 @@ use std::{ |
236 | time::Instant, |
237 | }; |
238 | |
239 | - use crate::{Error, Policy, Resolver, SpfOutput, SpfResult}; |
240 | + use crate::{Error, Resolver, SpfOutput, SpfResult}; |
241 | |
242 | use super::{Macro, Mechanism, Qualifier, Spf, Variables}; |
243 | |
244 | impl Resolver { |
245 | /// Verifies the SPF EHLO identity |
246 | - pub async fn verify_spf_helo(&self, ip: IpAddr, helo_domain: &str) -> SpfOutput { |
247 | + pub async fn verify_spf_helo( |
248 | + &self, |
249 | + ip: IpAddr, |
250 | + helo_domain: &str, |
251 | + host_domain: &str, |
252 | + ) -> SpfOutput { |
253 | if helo_domain.has_labels() { |
254 | self.check_host( |
255 | ip, |
256 | helo_domain, |
257 | helo_domain, |
258 | + host_domain, |
259 | &format!("postmaster@{}", helo_domain), |
260 | ) |
261 | .await |
262 | @@ -38,29 +44,36 @@ impl Resolver { |
263 | &self, |
264 | ip: IpAddr, |
265 | helo_domain: &str, |
266 | + host_domain: &str, |
267 | sender: &str, |
268 | ) -> SpfOutput { |
269 | self.check_host( |
270 | ip, |
271 | sender.rsplit_once('@').map_or(helo_domain, |(_, d)| d), |
272 | helo_domain, |
273 | + host_domain, |
274 | sender, |
275 | ) |
276 | .await |
277 | } |
278 | |
279 | /// Verifies both the SPF EHLO and MAIL FROM identities |
280 | - pub async fn verify_spf(&self, ip: IpAddr, helo_domain: &str, mail_from: &str) -> SpfOutput { |
281 | + pub async fn verify_spf( |
282 | + &self, |
283 | + ip: IpAddr, |
284 | + helo_domain: &str, |
285 | + host_domain: &str, |
286 | + mail_from: &str, |
287 | + ) -> SpfOutput { |
288 | // Verify HELO identity |
289 | - let output = self.verify_spf_helo(ip, helo_domain).await; |
290 | - match output.result() { |
291 | - SpfResult::TempError | SpfResult::Pass => (), |
292 | - SpfResult::None | SpfResult::PermError if self.verify_policy != Policy::VeryStrict => {} |
293 | - _ => return output, |
294 | + let output = self.verify_spf_helo(ip, helo_domain, host_domain).await; |
295 | + if matches!(output.result(), SpfResult::Pass) { |
296 | + // Verify MAIL FROM identity |
297 | + self.verify_spf_sender(ip, helo_domain, host_domain, mail_from) |
298 | + .await |
299 | + } else { |
300 | + output |
301 | } |
302 | - |
303 | - // Verify MAIL FROM identity |
304 | - self.verify_spf_sender(ip, helo_domain, mail_from).await |
305 | } |
306 | |
307 | #[allow(clippy::while_let_on_iterator)] |
308 | @@ -69,6 +82,7 @@ impl Resolver { |
309 | ip: IpAddr, |
310 | domain: &str, |
311 | helo_domain: &str, |
312 | + host_domain: &str, |
313 | sender: &str, |
314 | ) -> SpfOutput { |
315 | let output = SpfOutput::new(domain.to_string()); |
316 | @@ -84,7 +98,7 @@ impl Resolver { |
317 | vars.set_sender(format!("postmaster@{}", domain).into_bytes()); |
318 | } |
319 | vars.set_domain(domain.as_bytes()); |
320 | - vars.set_host_domain(self.host_domain.as_bytes()); |
321 | + vars.set_host_domain(host_domain.as_bytes()); |
322 | vars.set_helo_domain(helo_domain.as_bytes()); |
323 | |
324 | let mut lookup_limit = LookupLimit::new(); |
325 | @@ -625,7 +639,9 @@ mod test { |
326 | } else { |
327 | (value.try_into().unwrap(), "") |
328 | }; |
329 | - let output = resolver.verify_spf(client_ip, helo, mail_from).await; |
330 | + let output = resolver |
331 | + .verify_spf(client_ip, helo, "localdomain.org", mail_from) |
332 | + .await; |
333 | assert_eq!( |
334 | output.result(), |
335 | result, |