Commit
+84 -1 +/-3 browse
1 | diff --git a/CHANGELOG.md b/CHANGELOG.md |
2 | index 8505437..5d20967 100644 |
3 | --- a/CHANGELOG.md |
4 | +++ b/CHANGELOG.md |
5 | @@ -1,3 +1,8 @@ |
6 | + mail-auth 0.3.4 |
7 | + ================================ |
8 | + - Added `to_reverse_name` method to `IpAddr` to convert an IP address to a reverse DNS domain name. |
9 | + - Added `txt_raw_lookup` method to `Resolver` to perform a raw TXT lookup. |
10 | + |
11 | mail-auth 0.3.3 |
12 | ================================ |
13 | - Bump `mail-parser` dependency to 0.9 |
14 | diff --git a/Cargo.toml b/Cargo.toml |
15 | index a6e2250..897951b 100644 |
16 | --- a/Cargo.toml |
17 | +++ b/Cargo.toml |
18 | @@ -1,7 +1,7 @@ |
19 | [package] |
20 | name = "mail-auth" |
21 | description = "DKIM, ARC, SPF and DMARC library for Rust" |
22 | - version = "0.3.3" |
23 | + version = "0.3.4" |
24 | edition = "2021" |
25 | authors = [ "Stalwart Labs <hello@stalw.art>"] |
26 | license = "Apache-2.0 OR MIT" |
27 | diff --git a/src/common/resolver.rs b/src/common/resolver.rs |
28 | index 87b0e2c..4f70533 100644 |
29 | --- a/src/common/resolver.rs |
30 | +++ b/src/common/resolver.rs |
31 | @@ -100,6 +100,25 @@ impl Resolver { |
32 | }) |
33 | } |
34 | |
35 | + pub async fn txt_raw_lookup(&self, key: impl IntoFqdn<'_>) -> crate::Result<Vec<u8>> { |
36 | + let mut result = vec![]; |
37 | + for record in self |
38 | + .resolver |
39 | + .txt_lookup(key.into_fqdn().as_ref()) |
40 | + .await? |
41 | + .as_lookup() |
42 | + .record_iter() |
43 | + { |
44 | + if let Some(txt_data) = record.data().and_then(|r| r.as_txt()) { |
45 | + for item in txt_data.txt_data() { |
46 | + result.extend_from_slice(item); |
47 | + } |
48 | + } |
49 | + } |
50 | + |
51 | + Ok(result) |
52 | + } |
53 | + |
54 | pub async fn txt_lookup<'x, T: TxtRecordParser + Into<Txt> + UnwrapTxtRecord>( |
55 | &self, |
56 | key: impl IntoFqdn<'x>, |
57 | @@ -574,6 +593,41 @@ impl<'x> IntoFqdn<'x> for &String { |
58 | } |
59 | } |
60 | |
61 | + pub trait ToReverseName { |
62 | + fn to_reverse_name(&self) -> String; |
63 | + } |
64 | + |
65 | + impl ToReverseName for IpAddr { |
66 | + fn to_reverse_name(&self) -> String { |
67 | + use std::fmt::Write; |
68 | + |
69 | + match self { |
70 | + IpAddr::V4(ip) => { |
71 | + let mut segments = String::with_capacity(15); |
72 | + for octet in ip.octets().iter().rev() { |
73 | + if !segments.is_empty() { |
74 | + segments.push('.'); |
75 | + } |
76 | + let _ = write!(&mut segments, "{}", octet); |
77 | + } |
78 | + segments |
79 | + } |
80 | + IpAddr::V6(ip) => { |
81 | + let mut segments = String::with_capacity(63); |
82 | + for segment in ip.segments().iter().rev() { |
83 | + for &p in format!("{segment:04x}").as_bytes().iter().rev() { |
84 | + if !segments.is_empty() { |
85 | + segments.push('.'); |
86 | + } |
87 | + segments.push(char::from(p)); |
88 | + } |
89 | + } |
90 | + segments |
91 | + } |
92 | + } |
93 | + } |
94 | + } |
95 | + |
96 | #[cfg(any(test, feature = "test"))] |
97 | pub fn mock_resolve<T>(domain: &str) -> crate::Result<T> { |
98 | Err(if domain.contains("_parse_error.") { |
99 | @@ -586,3 +640,27 @@ pub fn mock_resolve<T>(domain: &str) -> crate::Result<T> { |
100 | Error::DnsRecordNotFound(trust_dns_resolver::proto::op::ResponseCode::NXDomain) |
101 | }) |
102 | } |
103 | + |
104 | + #[cfg(test)] |
105 | + mod test { |
106 | + use std::net::IpAddr; |
107 | + |
108 | + use crate::common::resolver::ToReverseName; |
109 | + |
110 | + #[test] |
111 | + fn reverse_lookup_addr() { |
112 | + for (addr, expected) in [ |
113 | + ("1.2.3.4", "4.3.2.1"), |
114 | + ( |
115 | + "2001:db8::cb01", |
116 | + "1.0.b.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2", |
117 | + ), |
118 | + ( |
119 | + "2a01:4f9:c011:b43c::1", |
120 | + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.c.3.4.b.1.1.0.c.9.f.4.0.1.0.a.2", |
121 | + ), |
122 | + ] { |
123 | + assert_eq!(addr.parse::<IpAddr>().unwrap().to_reverse_name(), expected); |
124 | + } |
125 | + } |
126 | + } |