Commit
Author: mdecimus [mauro@stalw.art]
Hash: 75ef956b35ff78aeeb73dc9149bf0b641224b755
Timestamp: Wed, 04 Oct 2023 06:24:29 +0000 (1 year ago)

+84 -1 +/-3 browse
v0.3.4
1diff --git a/CHANGELOG.md b/CHANGELOG.md
2index 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
15index 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
28index 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+ }