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}