Skip to content

Commit 42a8a95

Browse files
committed
Bring your own cache
1 parent db1928b commit 42a8a95

File tree

21 files changed

+1276
-627
lines changed

21 files changed

+1276
-627
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
mail-auth 0.6.0
2+
================================
3+
- Bring your own cache (or none at all): All validation functions can now take a `Parameters` struct that allows you to provide custom caches implementing the `ResolverCache` trait. By default no cache is used.
4+
15
mail-auth 0.5.1
26
================================
37
- Build `AuthenticatedMessage` from `mail-parser::Message`.

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "mail-auth"
33
description = "DKIM, ARC, SPF and DMARC library for Rust"
4-
version = "0.5.1"
4+
version = "0.6.0"
55
edition = "2021"
66
authors = [ "Stalwart Labs <[email protected]>"]
77
license = "Apache-2.0 OR MIT"
@@ -26,10 +26,8 @@ test = []
2626
ahash = "0.8.0"
2727
ed25519-dalek = { version = "2.0", optional = true }
2828
flate2 = "1.0.25"
29-
lru-cache = "0.1.2"
3029
mail-parser = { version = "0.9", features = ["ludicrous_mode", "full_encoding"] }
3130
mail-builder = { version = "0.3", features = ["ludicrous_mode"] }
32-
parking_lot = "0.12.0"
3331
quick-xml = { version = "0.37", optional = true }
3432
ring = { version = "0.17", optional = true }
3533
rsa = { version = "0.9.6", optional = true }
@@ -41,6 +39,7 @@ sha2 = { version = "0.10.6", features = ["oid"], optional = true }
4139
hickory-resolver = { version = "0.24", features = ["dns-over-rustls", "dnssec-ring"] }
4240
zip = "2.1.1"
4341
rand = { version = "0.8.5", optional = true }
42+
quick_cache = "0.6.9"
4443

4544
[dev-dependencies]
4645
tokio = { version = "1.16", features = ["net", "io-util", "time", "rt-multi-thread", "macros"] }

README.md

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ Features:
3535
### DKIM Signature Verification
3636

3737
```rust
38-
// Create a resolver using Cloudflare DNS
39-
let resolver = Resolver::new_cloudflare_tls().unwrap();
38+
// Create an authenticator using Cloudflare DNS
39+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
4040

4141
// Parse message
4242
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
4343

4444
// Validate signature
45-
let result = resolver.verify_dkim(&authenticated_message).await;
45+
let result = authenticator.verify_dkim(&authenticated_message).await;
4646

4747
// Make sure all signatures passed verification
4848
assert!(result.iter().all(|s| s.result() == &DkimResult::Pass));
@@ -85,14 +85,14 @@ Features:
8585
### ARC Chain Verification
8686

8787
```rust
88-
// Create a resolver using Cloudflare DNS
89-
let resolver = Resolver::new_cloudflare_tls().unwrap();
88+
// Create an authenticator using Cloudflare DNS
89+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
9090

9191
// Parse message
9292
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
9393

9494
// Validate ARC chain
95-
let result = resolver.verify_arc(&authenticated_message).await;
95+
let result = authenticator.verify_arc(&authenticated_message).await;
9696

9797
// Make sure ARC passed verification
9898
assert_eq!(result.result(), &DkimResult::Pass);
@@ -101,15 +101,15 @@ Features:
101101
### ARC Chain Sealing
102102

103103
```rust
104-
// Create a resolver using Cloudflare DNS
105-
let resolver = Resolver::new_cloudflare_tls().unwrap();
104+
// Create an authenticator using Cloudflare DNS
105+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
106106

107107
// Parse message to be sealed
108108
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
109109

110110
// Verify ARC and DKIM signatures
111-
let arc_result = resolver.verify_arc(&authenticated_message).await;
112-
let dkim_result = resolver.verify_dkim(&authenticated_message).await;
111+
let arc_result = authenticator.verify_arc(&authenticated_message).await;
112+
let dkim_result = authenticator.verify_dkim(&authenticated_message).await;
113113

114114
// Build Authenticated-Results header
115115
let auth_results = AuthenticationResults::new("mx.mydomain.org")
@@ -137,45 +137,61 @@ Features:
137137
### SPF Policy Evaluation
138138

139139
```rust
140-
// Create a resolver using Cloudflare DNS
141-
let resolver = Resolver::new_cloudflare_tls().unwrap();
140+
// Create an authenticator using Cloudflare DNS
141+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
142142

143143
// Verify HELO identity
144-
let result = resolver
145-
.verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com", "my-local-domain.org")
144+
let result = authenticator
145+
.verify_spf(SpfParameters::verify_ehlo(
146+
"127.0.0.1".parse().unwrap(),
147+
"gmail.com",
148+
"my-local-domain.org",
149+
))
146150
.await;
147151
assert_eq!(result.result(), SpfResult::Fail);
148152

149153
// Verify MAIL-FROM identity
150-
let result = resolver
151-
.verify_spf_sender("::1".parse().unwrap(), "gmail.com", "my-local-domain.org", "[email protected]")
154+
let result = authenticator
155+
.verify_spf(SpfParameters::verify_mail_from(
156+
"::1".parse().unwrap(),
157+
"gmail.com",
158+
"my-local-domain.org",
159+
160+
))
152161
.await;
153162
assert_eq!(result.result(), SpfResult::Fail);
154163
```
155164

156165
### DMARC Policy Evaluation
157166

158167
```rust
159-
// Create a resolver using Cloudflare DNS
160-
let resolver = Resolver::new_cloudflare_tls().unwrap();
168+
// Create an authenticator using Cloudflare DNS
169+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
161170

162171
// Verify DKIM signatures
163172
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
164-
let dkim_result = resolver.verify_dkim(&authenticated_message).await;
173+
let dkim_result = authenticator.verify_dkim(&authenticated_message).await;
165174

166175
// Verify SPF MAIL-FROM identity
167-
let spf_result = resolver
168-
.verify_spf_sender("::1".parse().unwrap(), "example.org", "my-local-domain.org", "[email protected]")
176+
let spf_result = authenticator
177+
.verify_spf(SpfParameters::verify_mail_from(
178+
"::1".parse().unwrap(),
179+
"example.org",
180+
"my-host-domain.org",
181+
182+
))
169183
.await;
170184

171185
// Verify DMARC
172-
let dmarc_result = resolver
186+
let dmarc_result = authenticator
173187
.verify_dmarc(
174-
&authenticated_message,
175-
&dkim_result,
176-
"example.org",
177-
&spf_result,
178-
|domain| psl::domain_str(domain).unwrap_or(domain),
188+
DmarcParameters::new(
189+
&authenticated_message,
190+
&dkim_result,
191+
"example.org",
192+
&spf_result,
193+
)
194+
.with_domain_suffix_fn(|domain| psl::domain_str(domain).unwrap_or(domain)),
179195
)
180196
.await;
181197
assert_eq!(dmarc_result.dkim_result(), &DmarcResult::Pass);

examples/arc_seal.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use mail_auth::{
1414
crypto::{RsaKey, Sha256},
1515
headers::HeaderWriter,
1616
},
17-
AuthenticatedMessage, AuthenticationResults, Resolver,
17+
AuthenticatedMessage, AuthenticationResults, MessageAuthenticator,
1818
};
1919

2020
const TEST_MESSAGE: &str = include_str!("../resources/arc/001.txt");
@@ -37,15 +37,15 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
3737

3838
#[tokio::main]
3939
async fn main() {
40-
// Create a resolver using Cloudflare DNS
41-
let resolver = Resolver::new_cloudflare_tls().unwrap();
40+
// Create an authenticator using Cloudflare DNS
41+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
4242

4343
// Parse message to be sealed
4444
let authenticated_message = AuthenticatedMessage::parse(TEST_MESSAGE.as_bytes()).unwrap();
4545

4646
// Verify ARC and DKIM signatures
47-
let arc_result = resolver.verify_arc(&authenticated_message).await;
48-
let dkim_result = resolver.verify_dkim(&authenticated_message).await;
47+
let arc_result = authenticator.verify_arc(&authenticated_message).await;
48+
let dkim_result = authenticator.verify_dkim(&authenticated_message).await;
4949

5050
// Build Authenticated-Results header
5151
let auth_results = AuthenticationResults::new("mx.mydomain.org")

examples/arc_verify.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@
88
* except according to those terms.
99
*/
1010

11-
use mail_auth::{AuthenticatedMessage, DkimResult, Resolver};
11+
use mail_auth::{AuthenticatedMessage, DkimResult, MessageAuthenticator};
1212

1313
const TEST_MESSAGE: &str = include_str!("../resources/arc/001.txt");
1414

1515
#[tokio::main]
1616
async fn main() {
17-
// Create a resolver using Cloudflare DNS
18-
let resolver = Resolver::new_cloudflare_tls().unwrap();
17+
// Create an authenticator using Cloudflare DNS
18+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
1919

2020
// Parse message
2121
let authenticated_message = AuthenticatedMessage::parse(TEST_MESSAGE.as_bytes()).unwrap();
2222

2323
// Validate ARC chain
24-
let result = resolver.verify_arc(&authenticated_message).await;
24+
let result = authenticator.verify_arc(&authenticated_message).await;
2525

2626
// Make sure ARC passed verification
2727
assert_eq!(result.result(), &DkimResult::Pass);

examples/dkim_verify.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* except according to those terms.
99
*/
1010

11-
use mail_auth::{AuthenticatedMessage, DkimResult, Resolver};
11+
use mail_auth::{AuthenticatedMessage, DkimResult, MessageAuthenticator};
1212

1313
const TEST_MESSAGE: &str = r#"DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed;
1414
d=football.example.com; [email protected];
@@ -39,14 +39,14 @@ Joe."#;
3939

4040
#[tokio::main]
4141
async fn main() {
42-
// Create a resolver using Cloudflare DNS
43-
let resolver = Resolver::new_cloudflare_tls().unwrap();
42+
// Create an authenticator using Cloudflare DNS
43+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
4444

4545
// Parse message
4646
let authenticated_message = AuthenticatedMessage::parse(TEST_MESSAGE.as_bytes()).unwrap();
4747

4848
// Validate signature
49-
let result = resolver.verify_dkim(&authenticated_message).await;
49+
let result = authenticator.verify_dkim(&authenticated_message).await;
5050

5151
// Make sure all signatures passed verification
5252
assert!(result.iter().all(|s| s.result() == &DkimResult::Pass));

examples/dmarc_verify.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
* except according to those terms.
99
*/
1010

11-
use mail_auth::{AuthenticatedMessage, DmarcResult, Resolver};
11+
use mail_auth::{
12+
dmarc::verify::DmarcParameters, spf::verify::SpfParameters, AuthenticatedMessage, DmarcResult,
13+
MessageAuthenticator,
14+
};
1215

1316
const TEST_MESSAGE: &str = r#"DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed;
1417
d=football.example.com; [email protected];
@@ -39,31 +42,33 @@ Joe."#;
3942

4043
#[tokio::main]
4144
async fn main() {
42-
// Create a resolver using Cloudflare DNS
43-
let resolver = Resolver::new_cloudflare_tls().unwrap();
45+
// Create an authenticator using Cloudflare DNS
46+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
4447

4548
// Verify DKIM signatures
4649
let authenticated_message = AuthenticatedMessage::parse(TEST_MESSAGE.as_bytes()).unwrap();
47-
let dkim_result = resolver.verify_dkim(&authenticated_message).await;
50+
let dkim_result = authenticator.verify_dkim(&authenticated_message).await;
4851

4952
// Verify SPF MAIL-FROM identity
50-
let spf_result = resolver
51-
.verify_spf_sender(
53+
let spf_result = authenticator
54+
.verify_spf(SpfParameters::verify_mail_from(
5255
"::1".parse().unwrap(),
5356
"example.org",
5457
"my-host-domain.org",
5558
56-
)
59+
))
5760
.await;
5861

5962
// Verify DMARC
60-
let dmarc_result = resolver
63+
let dmarc_result = authenticator
6164
.verify_dmarc(
62-
&authenticated_message,
63-
&dkim_result,
64-
"example.org",
65-
&spf_result,
66-
|domain| psl::domain_str(domain).unwrap_or(domain),
65+
DmarcParameters::new(
66+
&authenticated_message,
67+
&dkim_result,
68+
"example.org",
69+
&spf_result,
70+
)
71+
.with_domain_suffix_fn(|domain| psl::domain_str(domain).unwrap_or(domain)),
6772
)
6873
.await;
6974
assert_eq!(dmarc_result.dkim_result(), &DmarcResult::Pass);

examples/spf_verify.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,31 @@
88
* except according to those terms.
99
*/
1010

11-
use mail_auth::{Resolver, SpfResult};
11+
use mail_auth::{spf::verify::SpfParameters, MessageAuthenticator, SpfResult};
1212

1313
#[tokio::main]
1414
async fn main() {
15-
// Create a resolver using Cloudflare DNS
16-
let resolver = Resolver::new_cloudflare_tls().unwrap();
15+
// Create an authenticator using Cloudflare DNS
16+
let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
1717

1818
// Verify HELO identity
19-
let result = resolver
20-
.verify_spf_helo(
19+
let result = authenticator
20+
.verify_spf(SpfParameters::verify_ehlo(
2121
"127.0.0.1".parse().unwrap(),
2222
"gmail.com",
23-
"my-host-domain.org",
24-
)
23+
"my-local-domain.org",
24+
))
2525
.await;
2626
assert_eq!(result.result(), SpfResult::Fail);
2727

2828
// Verify MAIL-FROM identity
29-
let result = resolver
30-
.verify_spf_sender(
29+
let result = authenticator
30+
.verify_spf(SpfParameters::verify_mail_from(
3131
"::1".parse().unwrap(),
3232
"gmail.com",
33-
"my-host-domain.org",
33+
"my-local-domain.org",
3434
35-
)
35+
))
3636
.await;
3737
assert_eq!(result.result(), SpfResult::Fail);
3838
}

0 commit comments

Comments
 (0)