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