cdns_rs/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::*;
29use crate::common::{QType, DnsRdata};
30
31use super::query::QDns;
32use super::{QuerySetup, ResolveConfig};
33
34
35/// Resolves the A and AAAA query.
36///
37/// # Arguments
38///
39/// * `fqdn` - [AsRef][str] a domain name
40///
41/// # Returns
42///
43/// [CDnsResult] - Ok with inner type [Vec] [RecordSOA]. If vector is empty
44/// then no results found.
45///
46/// [CDnsResult] - Err with error description
47pub
48fn resolve_fqdn<C>(fqdn: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<IpAddr>>
49where C: AsRef<str>
50{
51 // forming request
52 let dns = QDns::make_a_aaaa_request(custom_resolv, fqdn.as_ref(), QuerySetup::default())?;
53
54 // sending request and receiving results
55 let res = dns.query();
56
57 // extracting data
58 let mut iplist: Vec<IpAddr> = vec![];
59
60
61 let recs = res.get_result()?;
62
63 for dnsr in recs.into_iter()
64 {
65 for resp in dnsr.into_iter()
66 {
67 match resp.borrow_rdata()
68 {
69 DnsRdata::A(ip) =>
70 iplist.push(IpAddr::from(ip.ip)),
71 DnsRdata::AAAA( ip ) =>
72 iplist.push(IpAddr::from(ip.ip)),
73 _ => continue,
74 }
75 }
76 }
77
78 return Ok(iplist);
79}
80
81/// Resolves the MX record by domain name. It returns a list of domains.
82/// The list is sorted by the `preference`.
83///
84/// # Arguments
85///
86/// * `fqdn` - [AsRef][str] a domain name
87///
88/// # Returns
89///
90/// [CDnsResult] - Ok with inner type [Vec] [Vec][String]. If vector is empty
91/// then no results found.
92///
93/// [CDnsResult] - Err with error description
94pub
95fn resolve_mx<C>(fqdn: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<String>>
96where C: AsRef<str>
97{
98 let mut dns_req =
99 QDns::make_empty(custom_resolv, QuerySetup::default())?;
100
101 dns_req.add_request(QType::MX, fqdn.as_ref());
102
103 // sending request and receiving results
104 let res = dns_req.query();
105
106 // extracting data
107 let mut mxlist: Vec<(u16, String)> = Vec::new();
108
109 let recs = res.get_result()?;
110
111 for dnsr in recs.into_iter()
112 {
113 for resp in dnsr.into_iter()
114 {
115 match resp.borrow_rdata()
116 {
117 DnsRdata::MX(mx) =>
118 {
119 //iterate and search for the suitable place
120 //let mut index: usize = 0;
121
122 let idx = mxlist.binary_search_by_key(&mx.preference, |x| x.0).map_or_else(|v| v, |f| f);
123 mxlist.insert(idx, (mx.preference, mx.exchange));
124
125 /*for (pref, _) in mxlist.iter()
126 {
127 if *pref >= mx.preference
128 {
129 break;
130 }
131
132 index += 1;
133 }
134
135 if index == mxlist.len()
136 {
137 // push back
138 mxlist.push((mx.preference, mx.exchange.clone()));
139 }
140 else
141 {
142 mxlist.insert(index, (*preference, exchange.clone()));
143 }*/
144 },
145 _ => continue,
146 }
147 }
148 }
149
150 return Ok(mxlist.into_iter().map( |(_, ip)| ip).collect());
151}
152
153/// Resolves the SOA record
154///
155/// # Arguments
156///
157/// * `fqdn` - [AsRef][str] a domain name
158///
159/// # Returns
160///
161/// [CDnsResult] - Ok with inner type [Vec] [RecordSOA]. If vector is empty
162/// then no results found.
163///
164/// [CDnsResult] - Err with error description
165pub
166fn resolve_soa<C>(fqdn: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<RecordSOA>>
167where C: AsRef<str>
168{
169 let mut dns_req =
170 QDns::make_empty(custom_resolv, QuerySetup::default())?;
171
172 dns_req.add_request(QType::SOA, fqdn.as_ref());
173
174 // sending request and receiving results
175 let res = dns_req.query();
176
177 let mut soa_list: Vec<RecordSOA> = Vec::new();
178
179 let recs = res.get_result()?;
180
181 for dnsr in recs.into_iter()
182 {
183 for resp in dnsr.into_iter()
184 {
185 match resp.borrow_rdata()
186 {
187 DnsRdata::SOA(soa) =>
188 soa_list.push(soa),
189 _ => continue,
190 }
191 }
192 }
193
194 return Ok(soa_list);
195}
196
197pub
198fn resolve_reverse<C>(fqdn: C, custom_resolv: Option<Arc<ResolveConfig>>) -> CDnsResult<Vec<String>>
199where C: AsRef<str>
200{
201 let mut dns_req =
202 QDns::make_empty(custom_resolv, QuerySetup::default())?;
203
204 dns_req.add_request(QType::PTR, fqdn.as_ref());
205
206 // sending request and receiving results
207 let res = dns_req.query();
208
209 let recs = res.get_result()?;
210
211 let mut ptr_list: Vec<String> = Vec::new();
212
213 for dnsr in recs.into_iter()
214 {
215 for resp in dnsr.into_iter()
216 {
217 match resp.borrow_rdata()
218 {
219 DnsRdata::PTR(ptr) =>
220 ptr_list.push(ptr.fqdn),
221 _ => continue,
222 }
223 }
224 }
225
226 return Ok(ptr_list);
227}
228
229#[cfg(test)]
230mod tests
231{
232 use crate::sync::request::{resolve_fqdn, resolve_mx};
233
234 #[test]
235 fn test_mx_resolve()
236 {
237 let mx_doms = resolve_mx("protonmail.com", None);
238
239 assert_eq!(mx_doms.is_err(), false);
240
241 let mx_doms = mx_doms.unwrap();
242
243 let mut index = 0;
244 for di in mx_doms
245 {
246 match index
247 {
248 0 => assert_eq!(di.as_str(), "mail.protonmail.ch"),
249 1 => assert_eq!(di.as_str(), "mailsec.protonmail.ch"),
250 _ => panic!("test is required to be modified")
251 }
252
253 index += 1;
254
255 println!("{}", di);
256 }
257
258 }
259
260
261 #[test]
262 fn test_a_aaaa_resolve()
263 {
264 let a_aaaa = resolve_fqdn("protonmail.com", None);
265
266 assert_eq!(a_aaaa.is_ok(), true);
267
268 let a_aaaa = a_aaaa.unwrap();
269
270 for di in a_aaaa
271 {
272 println!("{}", di);
273 }
274
275 }
276}