cdns_rs/a_sync/request.rs
1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 *
4 * Copyright (C) 2020 Aleksandr Morozov
5 *
6 * Copyright 2025 Aleksandr Morozov
7 *
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 *
11 * You may not use this work except in compliance with the Licence.
12 *
13 * You may obtain a copy of the Licence at:
14 *
15 * https://joinup.ec.europa.eu/software/page/eupl
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24use std::net::IpAddr;
25use std::sync::Arc;
26
27use crate::common::RecordSOA;
28use crate::error::*;
29
30use super::query::QDns;
31use super::DnsRdata;
32use super::QType;
33use super::{QuerySetup, ResolveConfig};
34
35/// Resolves the A and AAAA query.
36///
37/// # Arguments
38///
39/// * `fqdn` - [AsRef] [str] a domain name
40///
41/// * `custom_resolv` - a custom [ResolveConfig] wrapped in [Arc]
42///
43/// # Returns
44///
45/// [CDnsResult] - Ok with inner type [Vec] [RecordSOA]. If vector is empty
46/// then no results found.
47///
48/// [CDnsResult] - Err with error description
49pub async
50fn resolve_fqdn<C>(fqdn: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<IpAddr>>
51where C: AsRef<str>
52{
53 // forming request
54 let dns =
55 QDns::make_a_aaaa_request(custom_resolv, fqdn.as_ref(), QuerySetup::default()).await?;
56
57 // sending request and receiving results
58 let res = dns.query().await;
59
60 // extracting data
61 let mut iplist: Vec<IpAddr> = vec![];
62
63 let recs = res.get_result()?;
64
65 for dnsr in recs.into_iter()
66 {
67 for resp in dnsr.into_iter()
68 {
69 match resp.borrow_rdata()
70 {
71 DnsRdata::A(ip) =>
72 iplist.push(IpAddr::from(ip.ip)),
73 DnsRdata::AAAA( ip ) =>
74 iplist.push(IpAddr::from(ip.ip)),
75 _ => continue,
76 }
77 }
78 }
79
80 return Ok(iplist);
81}
82
83/// Resolves the MX record by domain name. It returns a list of domains.
84/// The list is sorted by the `preference`.
85///
86/// # Arguments
87///
88/// * `fqdn` - [AsRef] [str] a domain name
89///
90/// * `custom_resolv` - a custom [ResolveConfig] wrapped in [Arc]
91///
92/// # Returns
93///
94/// [CDnsResult] - Ok with inner type [Vec] [RecordSOA]. If vector is empty
95/// then no results found.
96///
97/// [CDnsResult] - Err with error description
98pub async
99fn resolve_mx<C>(fqdn: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<String>>
100where C: AsRef<str>
101{
102 let mut dns_req =
103 QDns::make_empty(custom_resolv, QuerySetup::default()).await?;
104
105 dns_req.add_request(QType::MX, fqdn.as_ref());
106
107 // sending request and receiving results
108 let res = dns_req.query().await;
109
110 // extracting data
111 let mut mxlist: Vec<(u16, String)> = Vec::new();
112
113 let recs = res.get_result()?;
114
115 for dnsr in recs.into_iter()
116 {
117 for resp in dnsr.into_iter()
118 {
119 match resp.borrow_rdata()
120 {
121 DnsRdata::MX(mx) =>
122 {
123 //iterate and search for the suitable place
124 //let mut index: usize = 0;
125
126 let idx = mxlist.binary_search_by_key(&mx.preference, |x| x.0).map_or_else(|v| v, |f| f);
127 mxlist.insert(idx, (mx.preference, mx.exchange));
128
129 /*for (pref, _) in mxlist.iter()
130 {
131 if *pref >= mx.preference
132 {
133 break;
134 }
135
136 index += 1;
137 }
138
139 if index == mxlist.len()
140 {
141 // push back
142 mxlist.push((mx.preference, mx.exchange.clone()));
143 }
144 else
145 {
146 mxlist.insert(index, (*preference, exchange.clone()));
147 }*/
148 },
149 _ => continue,
150 }
151 }
152 }
153
154 return Ok(mxlist.into_iter().map( |(_, ip)| ip).collect());
155}
156
157/// Resolves the SOA record
158///
159/// # Arguments
160///
161/// * `fqdn` - [AsRef] [str] a domain name
162///
163/// * `custom_resolv` - a custom [ResolveConfig] wrapped in [Arc]
164///
165/// # Returns
166///
167/// [CDnsResult] - Ok with inner type [Vec] [RecordSOA]. If vector is empty
168/// then no results found.
169///
170/// [CDnsResult] - Err with error description
171pub async
172fn resolve_soa<C>(fqdn: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<RecordSOA>>
173where C: AsRef<str>
174{
175 let mut dns_req =
176 QDns::make_empty(custom_resolv, QuerySetup::default()).await?;
177
178 dns_req.add_request(QType::SOA, fqdn.as_ref());
179
180 // sending request and receiving results
181 let res = dns_req.query().await;
182
183 let mut soa_list: Vec<RecordSOA> = Vec::new();
184
185 let recs = res.get_result()?;
186
187 for dnsr in recs.into_iter()
188 {
189 for resp in dnsr.into_iter()
190 {
191 match resp.borrow_rdata()
192 {
193 DnsRdata::SOA(soa) =>
194 soa_list.push(soa),
195 _ => continue,
196 }
197 }
198 }
199
200 return Ok(soa_list);
201}
202
203/// Resolves the IP address to FQDN
204///
205/// # Arguments
206///
207/// * `ipaddr` - [AsRef] [str] an IP address
208///
209/// * `custom_resolv` - a custom [ResolveConfig] wrapped in [Arc]
210///
211/// # Returns
212///
213/// [CDnsResult] - Ok with inner type [Vec] [RecordSOA]. If vector is empty
214/// then no results found.
215///
216/// [CDnsResult] - Err with error description
217pub async
218fn resolve_reverse<C>(ipaddr: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<String>>
219where C: AsRef<str>
220{
221 let mut dns_req =
222 QDns::make_empty(custom_resolv, QuerySetup::default()).await?;
223
224 dns_req.add_request(QType::PTR, ipaddr.as_ref());
225
226 // sending request and receiving results
227 let res = dns_req.query().await;
228
229 let recs = res.get_result()?;
230
231 let mut ptr_list: Vec<String> = Vec::new();
232
233 for dnsr in recs.into_iter()
234 {
235 for resp in dnsr.into_iter()
236 {
237 match resp.borrow_rdata()
238 {
239 DnsRdata::PTR(ptr) =>
240 ptr_list.push(ptr.fqdn),
241 _ => continue,
242 }
243 }
244 }
245
246 return Ok(ptr_list);
247}
248
249
250#[cfg(test)]
251mod tests
252{
253 use crate::a_sync::request::{resolve_fqdn, resolve_mx, resolve_reverse};
254
255 #[tokio::test]
256 async fn test_mx_resolve()
257 {
258 let mx_doms = resolve_mx("protonmail.com", None).await;
259
260 assert_eq!(mx_doms.is_err(), false);
261
262 let mx_doms = mx_doms.unwrap();
263
264 let mut index = 0;
265 for di in mx_doms
266 {
267 match index
268 {
269 0 => assert_eq!(di.as_str(), "mail.protonmail.ch"),
270 1 => assert_eq!(di.as_str(), "mailsec.protonmail.ch"),
271 _ => panic!("test is required to be modified")
272 }
273
274 index += 1;
275
276 println!("{}", di);
277 }
278
279 }
280
281
282 #[tokio::test]
283 async fn test_a_aaaa_resolve()
284 {
285 let a_aaaa = resolve_fqdn("protonmail.com", None).await;
286
287 assert_eq!(a_aaaa.is_ok(), true);
288
289 let a_aaaa = a_aaaa.unwrap();
290
291 for di in a_aaaa
292 {
293 println!("{}", di);
294 }
295
296 }
297
298 #[tokio::test]
299 async fn test_ptr_resolve_fail()
300 {
301 let ptr = resolve_reverse("protonmail.com", None).await;
302
303 assert_eq!(ptr.is_ok(), true);
304
305 let ptr = ptr.unwrap();
306
307 assert_eq!(ptr.len(), 0);
308
309 }
310}