Commit
Author: Mauro D [mauro@stalw.art]
Hash: 4383fcc008014d0ebf2e798166de0166f9aa2982
Timestamp: Tue, 17 Jan 2023 15:31:37 +0000 (1 year ago)

+173 -149 +/-13 browse
Moved mock resolver under test feature.
1diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
2index 387b9c4..7739fab 100644
3--- a/.github/workflows/rust.yml
4+++ b/.github/workflows/rust.yml
5 @@ -30,4 +30,4 @@ jobs:
6 - name: Build
7 run: cargo build --verbose
8 - name: Run tests
9- run: cargo test --verbose
10+ run: cargo test --features test --verbose
11 diff --git a/Cargo.toml b/Cargo.toml
12index 18c3f76..0595c70 100644
13--- a/Cargo.toml
14+++ b/Cargo.toml
15 @@ -34,3 +34,6 @@ flate2 = "1.0.25"
16
17 [dev-dependencies]
18 tokio = { version = "1.16", features = ["net", "io-util", "time", "rt-multi-thread", "macros"] }
19+
20+ [features]
21+ test = []
22 diff --git a/README.md b/README.md
23index 3e75e48..8e9ff73 100644
24--- a/README.md
25+++ b/README.md
26 @@ -187,7 +187,7 @@ More examples available under the [examples](examples) directory.
27 To run the testsuite:
28
29 ```bash
30- $ cargo test --all-features
31+ $ cargo test --features test
32 ```
33
34 To fuzz the library with `cargo-fuzz`:
35 diff --git a/src/arc/seal.rs b/src/arc/seal.rs
36index 101c620..9ab8d54 100644
37--- a/src/arc/seal.rs
38+++ b/src/arc/seal.rs
39 @@ -177,6 +177,7 @@ impl Signature {
40 }
41
42 #[cfg(test)]
43+ #[allow(unused)]
44 mod test {
45 use std::time::{Duration, Instant};
46
47 @@ -235,16 +236,19 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
48
49 // Crate resolver
50 let resolver = Resolver::new_system_conf().unwrap();
51- resolver.txt_add(
52- "rsa._domainkey.manchego.org.".to_string(),
53- DomainKey::parse(RSA_PUBLIC_KEY.as_bytes()).unwrap(),
54- Instant::now() + Duration::new(3600, 0),
55- );
56- resolver.txt_add(
57- "ed._domainkey.scamorza.org.".to_string(),
58- DomainKey::parse(ED25519_PUBLIC_KEY.as_bytes()).unwrap(),
59- Instant::now() + Duration::new(3600, 0),
60- );
61+ #[cfg(feature = "test")]
62+ {
63+ resolver.txt_add(
64+ "rsa._domainkey.manchego.org.".to_string(),
65+ DomainKey::parse(RSA_PUBLIC_KEY.as_bytes()).unwrap(),
66+ Instant::now() + Duration::new(3600, 0),
67+ );
68+ resolver.txt_add(
69+ "ed._domainkey.scamorza.org.".to_string(),
70+ DomainKey::parse(ED25519_PUBLIC_KEY.as_bytes()).unwrap(),
71+ Instant::now() + Duration::new(3600, 0),
72+ );
73+ }
74
75 // Create private keys
76 let pk_ed_public =
77 diff --git a/src/arc/verify.rs b/src/arc/verify.rs
78index 3322feb..ef44a3c 100644
79--- a/src/arc/verify.rs
80+++ b/src/arc/verify.rs
81 @@ -175,6 +175,7 @@ impl Resolver {
82 }
83
84 #[cfg(test)]
85+ #[allow(unused)]
86 mod test {
87 use std::{
88 fs,
89 @@ -220,6 +221,7 @@ mod test {
90 .split('\n')
91 .filter_map(|r| r.split_once(' ').map(|(a, b)| (a, b.as_bytes())))
92 {
93+ #[cfg(feature = "test")]
94 resolver.txt_add(
95 format!("{}.", key),
96 DomainKey::parse(value).unwrap(),
97 diff --git a/src/common/resolver.rs b/src/common/resolver.rs
98index 28d355a..7cb4a61 100644
99--- a/src/common/resolver.rs
100+++ b/src/common/resolver.rs
101 @@ -109,7 +109,7 @@ impl Resolver {
102 return T::unwrap_txt(value);
103 }
104
105- #[cfg(test)]
106+ #[cfg(feature = "test")]
107 if true {
108 return mock_resolve(key.as_ref());
109 }
110 @@ -150,7 +150,7 @@ impl Resolver {
111 return Ok(value);
112 }
113
114- #[cfg(test)]
115+ #[cfg(feature = "test")]
116 if true {
117 return mock_resolve(key.as_ref());
118 }
119 @@ -190,7 +190,7 @@ impl Resolver {
120 return Ok(value);
121 }
122
123- #[cfg(test)]
124+ #[cfg(feature = "test")]
125 if true {
126 return mock_resolve(key.as_ref());
127 }
128 @@ -216,7 +216,7 @@ impl Resolver {
129 return Ok(value);
130 }
131
132- #[cfg(test)]
133+ #[cfg(feature = "test")]
134 if true {
135 return mock_resolve(key.as_ref());
136 }
137 @@ -246,7 +246,7 @@ impl Resolver {
138 return Ok(value);
139 }
140
141- #[cfg(test)]
142+ #[cfg(feature = "test")]
143 if true {
144 return mock_resolve(&addr.to_string());
145 }
146 @@ -270,8 +270,8 @@ impl Resolver {
147 .insert(addr, Arc::new(ptr), ptr_lookup.valid_until()))
148 }
149
150- pub(crate) async fn exists<'x>(&self, key: impl IntoFqdn<'x>) -> crate::Result<bool> {
151- #[cfg(test)]
152+ pub async fn exists<'x>(&self, key: impl IntoFqdn<'x>) -> crate::Result<bool> {
153+ #[cfg(feature = "test")]
154 if true {
155 let key = key.into_fqdn().into_owned();
156 return match self.ipv4_lookup(key.as_str()).await {
157 @@ -302,8 +302,8 @@ impl Resolver {
158 }
159 }
160
161- #[cfg(test)]
162- pub(crate) fn txt_add<'x>(
163+ #[cfg(feature = "test")]
164+ pub fn txt_add<'x>(
165 &self,
166 name: impl IntoFqdn<'x>,
167 value: impl Into<Txt>,
168 @@ -313,8 +313,8 @@ impl Resolver {
169 .insert(name.into_fqdn().into_owned(), value.into(), valid_until);
170 }
171
172- #[cfg(test)]
173- pub(crate) fn ipv4_add<'x>(
174+ #[cfg(feature = "test")]
175+ pub fn ipv4_add<'x>(
176 &self,
177 name: impl IntoFqdn<'x>,
178 value: Vec<Ipv4Addr>,
179 @@ -324,8 +324,8 @@ impl Resolver {
180 .insert(name.into_fqdn().into_owned(), Arc::new(value), valid_until);
181 }
182
183- #[cfg(test)]
184- pub(crate) fn ipv6_add<'x>(
185+ #[cfg(feature = "test")]
186+ pub fn ipv6_add<'x>(
187 &self,
188 name: impl IntoFqdn<'x>,
189 value: Vec<Ipv6Addr>,
190 @@ -335,18 +335,13 @@ impl Resolver {
191 .insert(name.into_fqdn().into_owned(), Arc::new(value), valid_until);
192 }
193
194- #[cfg(test)]
195- pub(crate) fn ptr_add(
196- &self,
197- name: IpAddr,
198- value: Vec<String>,
199- valid_until: std::time::Instant,
200- ) {
201+ #[cfg(feature = "test")]
202+ pub fn ptr_add(&self, name: IpAddr, value: Vec<String>, valid_until: std::time::Instant) {
203 self.cache_ptr.insert(name, Arc::new(value), valid_until);
204 }
205
206- #[cfg(test)]
207- pub(crate) fn mx_add<'x>(
208+ #[cfg(feature = "test")]
209+ pub fn mx_add<'x>(
210 &self,
211 name: impl IntoFqdn<'x>,
212 value: Vec<MX>,
213 @@ -543,8 +538,8 @@ impl<'x> IntoFqdn<'x> for &String {
214 }
215 }
216
217- #[cfg(test)]
218- fn mock_resolve<T>(domain: &str) -> crate::Result<T> {
219+ #[cfg(feature = "test")]
220+ pub fn mock_resolve<T>(domain: &str) -> crate::Result<T> {
221 Err(if domain.contains("_parse_error.") {
222 Error::ParseError
223 } else if domain.contains("_invalid_record.") {
224 diff --git a/src/dkim/sign.rs b/src/dkim/sign.rs
225index 1aa996e..819af62 100644
226--- a/src/dkim/sign.rs
227+++ b/src/dkim/sign.rs
228 @@ -94,6 +94,7 @@ impl<T: SigningKey> DkimSigner<T, Done> {
229 }
230
231 #[cfg(test)]
232+ #[allow(unused)]
233 mod test {
234 use std::time::{Duration, Instant};
235
236 @@ -206,21 +207,24 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
237
238 // Create resolver
239 let resolver = Resolver::new_system_conf().unwrap();
240- resolver.txt_add(
241- "default._domainkey.example.com.".to_string(),
242- DomainKey::parse(RSA_PUBLIC_KEY.as_bytes()).unwrap(),
243- Instant::now() + Duration::new(3600, 0),
244- );
245- resolver.txt_add(
246- "ed._domainkey.example.com.".to_string(),
247- DomainKey::parse(ED25519_PUBLIC_KEY.as_bytes()).unwrap(),
248- Instant::now() + Duration::new(3600, 0),
249- );
250- resolver.txt_add(
251- "_report._domainkey.example.com.".to_string(),
252- DomainKeyReport::parse("ra=dkim-failures; rp=100; rr=x".as_bytes()).unwrap(),
253- Instant::now() + Duration::new(3600, 0),
254- );
255+ #[cfg(feature = "test")]
256+ {
257+ resolver.txt_add(
258+ "default._domainkey.example.com.".to_string(),
259+ DomainKey::parse(RSA_PUBLIC_KEY.as_bytes()).unwrap(),
260+ Instant::now() + Duration::new(3600, 0),
261+ );
262+ resolver.txt_add(
263+ "ed._domainkey.example.com.".to_string(),
264+ DomainKey::parse(ED25519_PUBLIC_KEY.as_bytes()).unwrap(),
265+ Instant::now() + Duration::new(3600, 0),
266+ );
267+ resolver.txt_add(
268+ "_report._domainkey.example.com.".to_string(),
269+ DomainKeyReport::parse("ra=dkim-failures; rp=100; rr=x".as_bytes()).unwrap(),
270+ Instant::now() + Duration::new(3600, 0),
271+ );
272+ }
273
274 // Test RSA-SHA256 relaxed/relaxed
275 let pk_rsa = RsaKey::<Sha256>::from_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
276 @@ -348,6 +352,7 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
277
278 // Verify ATPS (success)
279 let pk_rsa = RsaKey::<Sha256>::from_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
280+ #[cfg(feature = "test")]
281 resolver.txt_add(
282 "UN42N5XOV642KXRXRQIYANHCOUPGQL5LT4WTBKYT2IJFLBWODFDQ._atps.example.com.".to_string(),
283 Atps::parse(b"v=ATPS1;").unwrap(),
284 @@ -370,6 +375,7 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
285
286 // Verify ATPS (success - no hash)
287 let pk_rsa = RsaKey::<Sha256>::from_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
288+ #[cfg(feature = "test")]
289 resolver.txt_add(
290 "example.com._atps.example.com.".to_string(),
291 Atps::parse(b"v=ATPS1;").unwrap(),
292 diff --git a/src/dkim/verify.rs b/src/dkim/verify.rs
293index 26099f9..1e80d44 100644
294--- a/src/dkim/verify.rs
295+++ b/src/dkim/verify.rs
296 @@ -353,6 +353,7 @@ impl Verifier for &[u8] {
297 }
298
299 #[cfg(test)]
300+ #[allow(unused)]
301 mod test {
302 use std::{
303 fs,
304 @@ -412,6 +413,7 @@ mod test {
305 .split('\n')
306 .filter_map(|r| r.split_once(' ').map(|(a, b)| (a, b.as_bytes())))
307 {
308+ #[cfg(feature = "test")]
309 resolver.txt_add(
310 format!("{}.", key),
311 DomainKey::parse(value).unwrap(),
312 diff --git a/src/dmarc/verify.rs b/src/dmarc/verify.rs
313index 2521bf5..21a3287 100644
314--- a/src/dmarc/verify.rs
315+++ b/src/dmarc/verify.rs
316 @@ -188,6 +188,7 @@ impl Resolver {
317 }
318
319 #[cfg(test)]
320+ #[allow(unused)]
321 mod test {
322 use std::time::{Duration, Instant};
323
324 @@ -312,6 +313,7 @@ mod test {
325 Policy::Reject,
326 ),
327 ] {
328+ #[cfg(feature = "test")]
329 resolver.txt_add(
330 dmarc_dns,
331 Dmarc::parse(dmarc.as_bytes()).unwrap(),
332 @@ -347,6 +349,7 @@ mod test {
333 #[tokio::test]
334 async fn dmarc_verify_report_address() {
335 let resolver = Resolver::new_system_conf().unwrap();
336+ #[cfg(feature = "test")]
337 resolver.txt_add(
338 "example.org.report.dmarc.external.org.",
339 Dmarc::parse(b"v=DMARC1").unwrap(),
340 diff --git a/src/lib.rs b/src/lib.rs
341index aff4e34..5ef3194 100644
342--- a/src/lib.rs
343+++ b/src/lib.rs
344 @@ -198,7 +198,7 @@
345 //! To run the testsuite:
346 //!
347 //! ```bash
348- //! $ cargo test --all-features
349+ //! $ cargo test --features test
350 //! ```
351 //!
352 //! To fuzz the library with `cargo-fuzz`:
353 @@ -286,6 +286,7 @@ pub use flate2;
354 pub use sha1;
355 pub use sha2;
356 pub use trust_dns_resolver;
357+ pub use zip;
358
359 pub struct Resolver {
360 pub(crate) resolver: TokioAsyncResolver,
361 diff --git a/src/report/arf/mod.rs b/src/report/arf/mod.rs
362index c73b3da..f191d4e 100644
363--- a/src/report/arf/mod.rs
364+++ b/src/report/arf/mod.rs
365 @@ -29,6 +29,10 @@ impl<'x> Feedback<'x> {
366 self.original_envelope_id.as_deref()
367 }
368
369+ pub fn feedback_type(&self) -> FeedbackType {
370+ self.feedback_type
371+ }
372+
373 pub fn with_original_envelope_id(mut self, value: impl Into<Cow<'x, str>>) -> Self {
374 self.original_envelope_id = Some(value.into());
375 self
376 diff --git a/src/report/tlsrpt/mod.rs b/src/report/tlsrpt/mod.rs
377index 1cf4c1d..fc50330 100644
378--- a/src/report/tlsrpt/mod.rs
379+++ b/src/report/tlsrpt/mod.rs
380 @@ -133,7 +133,7 @@ pub enum PolicyType {
381 Other,
382 }
383
384- #[derive(Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize)]
385+ #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
386 pub enum ResultType {
387 #[serde(rename = "starttls-not-supported")]
388 StartTlsNotSupported,
389 diff --git a/src/spf/verify.rs b/src/spf/verify.rs
390index a5610bf..7337fe9 100644
391--- a/src/spf/verify.rs
392+++ b/src/spf/verify.rs
393 @@ -513,6 +513,7 @@ impl HasLabels for &str {
394 }
395
396 #[cfg(test)]
397+ #[allow(unused)]
398 mod test {
399
400 use std::{
401 @@ -557,104 +558,107 @@ mod test {
402 line
403 };
404
405- if let Some(name) = line.strip_prefix("name:") {
406- test_name = name.trim();
407- } else if let Some(record) = line.strip_prefix("spf:") {
408- let (name, record) = record.trim().split_once(' ').unwrap();
409- resolver.txt_add(
410- name.trim().to_string(),
411- Spf::parse(record.as_bytes()),
412- valid_until,
413- );
414- } else if let Some(record) = line.strip_prefix("exp:") {
415- let (name, record) = record.trim().split_once(' ').unwrap();
416- resolver.txt_add(
417- name.trim().to_string(),
418- Macro::parse(record.as_bytes()),
419- valid_until,
420- );
421- } else if let Some(record) = line.strip_prefix("a:") {
422- let (name, record) = record.trim().split_once(' ').unwrap();
423- resolver.ipv4_add(
424- name.trim().to_string(),
425- record
426- .split(',')
427- .map(|item| item.trim().parse::<Ipv4Addr>().unwrap())
428- .collect(),
429- valid_until,
430- );
431- } else if let Some(record) = line.strip_prefix("aaaa:") {
432- let (name, record) = record.trim().split_once(' ').unwrap();
433- resolver.ipv6_add(
434- name.trim().to_string(),
435- record
436- .split(',')
437- .map(|item| item.trim().parse::<Ipv6Addr>().unwrap())
438- .collect(),
439- valid_until,
440- );
441- } else if let Some(record) = line.strip_prefix("ptr:") {
442- let (name, record) = record.trim().split_once(' ').unwrap();
443- resolver.ptr_add(
444- name.trim().parse::<IpAddr>().unwrap(),
445- record
446- .split(',')
447- .map(|item| item.trim().to_string())
448- .collect(),
449- valid_until,
450- );
451- } else if let Some(record) = line.strip_prefix("mx:") {
452- let (name, record) = record.trim().split_once(' ').unwrap();
453- let mut mxs = Vec::new();
454- for (pos, item) in record.split(',').enumerate() {
455- let ip = item.trim().parse::<IpAddr>().unwrap();
456- let mx_name = format!("mx.{}.{}", ip, pos);
457- match ip {
458- IpAddr::V4(ip) => {
459- resolver.ipv4_add(mx_name.clone(), vec![ip], valid_until)
460- }
461- IpAddr::V6(ip) => {
462- resolver.ipv6_add(mx_name.clone(), vec![ip], valid_until)
463+ #[cfg(feature = "test")]
464+ {
465+ if let Some(name) = line.strip_prefix("name:") {
466+ test_name = name.trim();
467+ } else if let Some(record) = line.strip_prefix("spf:") {
468+ let (name, record) = record.trim().split_once(' ').unwrap();
469+ resolver.txt_add(
470+ name.trim().to_string(),
471+ Spf::parse(record.as_bytes()),
472+ valid_until,
473+ );
474+ } else if let Some(record) = line.strip_prefix("exp:") {
475+ let (name, record) = record.trim().split_once(' ').unwrap();
476+ resolver.txt_add(
477+ name.trim().to_string(),
478+ Macro::parse(record.as_bytes()),
479+ valid_until,
480+ );
481+ } else if let Some(record) = line.strip_prefix("a:") {
482+ let (name, record) = record.trim().split_once(' ').unwrap();
483+ resolver.ipv4_add(
484+ name.trim().to_string(),
485+ record
486+ .split(',')
487+ .map(|item| item.trim().parse::<Ipv4Addr>().unwrap())
488+ .collect(),
489+ valid_until,
490+ );
491+ } else if let Some(record) = line.strip_prefix("aaaa:") {
492+ let (name, record) = record.trim().split_once(' ').unwrap();
493+ resolver.ipv6_add(
494+ name.trim().to_string(),
495+ record
496+ .split(',')
497+ .map(|item| item.trim().parse::<Ipv6Addr>().unwrap())
498+ .collect(),
499+ valid_until,
500+ );
501+ } else if let Some(record) = line.strip_prefix("ptr:") {
502+ let (name, record) = record.trim().split_once(' ').unwrap();
503+ resolver.ptr_add(
504+ name.trim().parse::<IpAddr>().unwrap(),
505+ record
506+ .split(',')
507+ .map(|item| item.trim().to_string())
508+ .collect(),
509+ valid_until,
510+ );
511+ } else if let Some(record) = line.strip_prefix("mx:") {
512+ let (name, record) = record.trim().split_once(' ').unwrap();
513+ let mut mxs = Vec::new();
514+ for (pos, item) in record.split(',').enumerate() {
515+ let ip = item.trim().parse::<IpAddr>().unwrap();
516+ let mx_name = format!("mx.{}.{}", ip, pos);
517+ match ip {
518+ IpAddr::V4(ip) => {
519+ resolver.ipv4_add(mx_name.clone(), vec![ip], valid_until)
520+ }
521+ IpAddr::V6(ip) => {
522+ resolver.ipv6_add(mx_name.clone(), vec![ip], valid_until)
523+ }
524 }
525+ mxs.push(MX {
526+ exchanges: vec![mx_name],
527+ preference: (pos + 1) as u16,
528+ });
529+ }
530+ resolver.mx_add(name.trim().to_string(), mxs, valid_until);
531+ } else if let Some(value) = line.strip_prefix("domain:") {
532+ helo = value.trim();
533+ } else if let Some(value) = line.strip_prefix("sender:") {
534+ mail_from = value.trim();
535+ } else if let Some(value) = line.strip_prefix("ip:") {
536+ client_ip = value.trim().parse().unwrap();
537+ } else if let Some(value) = line.strip_prefix("expect:") {
538+ let value = value.trim();
539+ let (result, exp): (SpfResult, &str) =
540+ if let Some((result, exp)) = value.split_once(' ') {
541+ (result.trim().try_into().unwrap(), exp.trim())
542+ } else {
543+ (value.try_into().unwrap(), "")
544+ };
545+ let output = resolver
546+ .verify_spf(client_ip, helo, "localdomain.org", mail_from)
547+ .await;
548+ assert_eq!(
549+ output.result(),
550+ result,
551+ "Failed for {:?}, test {}.",
552+ test_name,
553+ test_num,
554+ );
555+
556+ if !exp.is_empty() {
557+ assert_eq!(Some(exp.to_string()).as_deref(), output.explanation());
558+ }
559+ test_num += 1;
560+ if test_name != last_test_name {
561+ println!("Passed test {:?}", test_name);
562+ last_test_name = test_name;
563 }
564- mxs.push(MX {
565- exchanges: vec![mx_name],
566- preference: (pos + 1) as u16,
567- });
568- }
569- resolver.mx_add(name.trim().to_string(), mxs, valid_until);
570- } else if let Some(value) = line.strip_prefix("domain:") {
571- helo = value.trim();
572- } else if let Some(value) = line.strip_prefix("sender:") {
573- mail_from = value.trim();
574- } else if let Some(value) = line.strip_prefix("ip:") {
575- client_ip = value.trim().parse().unwrap();
576- } else if let Some(value) = line.strip_prefix("expect:") {
577- let value = value.trim();
578- let (result, exp): (SpfResult, &str) =
579- if let Some((result, exp)) = value.split_once(' ') {
580- (result.trim().try_into().unwrap(), exp.trim())
581- } else {
582- (value.try_into().unwrap(), "")
583- };
584- let output = resolver
585- .verify_spf(client_ip, helo, "localdomain.org", mail_from)
586- .await;
587- assert_eq!(
588- output.result(),
589- result,
590- "Failed for {:?}, test {}.",
591- test_name,
592- test_num,
593- );
594-
595- if !exp.is_empty() {
596- assert_eq!(Some(exp.to_string()).as_deref(), output.explanation());
597- }
598- test_num += 1;
599- if test_name != last_test_name {
600- println!("Passed test {:?}", test_name);
601- last_test_name = test_name;
602 }
603 }
604 }