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