cdns_rs/a_sync/
query.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
24
25use std::collections::{LinkedList, HashSet};
26use std::convert::TryFrom;
27use std::net::IpAddr;
28use std::sync::Arc;
29use std::time::Duration;
30use std::time::Instant;
31
32use async_recursion::async_recursion;
33use tokio::task::JoinHandle;
34
35use crate::cfg_resolv_parser::{ResolveConfigFamily, ResolveConfEntry};
36use crate::error::*;
37use crate::query::{QDnsQuery, QDnsQueriesRes, QDnsQueryRec};
38use crate::sync::QuerySetup;
39use crate::{write_error, internal_error, internal_error_map};
40use crate::query_private::QDnsReq;
41
42use super::common::*;
43use super::caches::CACHE;
44use super::network::{new_udp, new_tcp, NetworkTapType};
45use super::query_async_taps::{AsyncTaps, Tap};
46use super::ResolveConfig;
47
48
49enum SpawnFutereRes
50{
51    Ok(QDnsQuery),
52    Truncated(DnsRequestHeader),
53}
54
55
56/// A main instance which contains all common logic.
57pub struct QDns
58{
59    /// An instance of the parser /etc/resolv.conf or custom config
60    resolvers: Arc<ResolveConfig>,
61    /// A pre-ordered list of the requests, if more than one
62    ordered_req_list: Vec<QDnsReq>,
63    /// Override options
64    opts: QuerySetup,
65}
66
67impl QDns
68{
69    /// Initializes new empty storage for requests.
70    /// 
71    /// In some cases it is good idea to combine different requests, because by default
72    /// all requests are performed in parallel. But in some cases it is bad idea.
73    /// 
74    /// # Arguments
75    /// 
76    /// * `resolvers` - an [Arc] [ResolveConfig] which contains configuration i.e nameservers
77    /// 
78    /// * `planned_reqs_len` - how many requests are planned
79    /// 
80    /// * `opts` - [QuerySetup] additional options or overrides. Use default() for default
81    ///     values.
82    /// 
83    /// # Returns
84    /// 
85    /// Never panics. Returns Self.
86    pub 
87    fn make_empty(resolvers: Arc<ResolveConfig>, planned_reqs_len: usize, opts: QuerySetup) -> QDns
88    {
89        return 
90            Self
91            {
92                resolvers: resolvers,
93                ordered_req_list: Vec::with_capacity(planned_reqs_len),
94                opts: opts,
95            };
96    }
97
98    /// Adds new request to previously created empty storage for request with [QDns::make_empty].
99    /// 
100    /// # Arguemnts
101    /// 
102    /// * `qtype` - a [QType] type of the request
103    /// 
104    /// * `req_name` - a [Into] [QDnsName] which is target. i.e 'localhost' or 'domain.tld'
105    /// 
106    /// # Returns
107    /// 
108    /// * [CDnsResult] - Ok with nothing in inner type
109    /// 
110    /// * [CDnsResult] - Err with error description
111    pub 
112    fn add_request<R>(&mut self, qtype: QType, req_name: R)
113    where R: Into<QDnsName>
114    {
115        let qr = QDnsReq::new(req_name.into(), qtype);
116
117        self.ordered_req_list.push(qr);
118
119        return;
120    }
121
122    /// This is helper which makes for you an A, AAAA query. The order of A and AAAA and
123    /// which from both are allowed is defined in the [ResolveConfig].
124    /// 
125    /// Use this function directly. Do not use [QDns::make_empty]
126    /// 
127    /// # Arguments
128    /// 
129    /// * `resolvers` - an [Arc] [ResolveConfig] which contains configuration i.e nameservers
130    /// 
131    /// * `req_name` - a [Into] [QDnsName] which is target i.e 'localhost' or 'domain.tld'
132    /// 
133    /// * `opts` - [QuerySetup] additional options or overrides. Use default() for default
134    ///     values.
135    /// 
136    /// # Returns
137    /// 
138    /// * [CDnsResult] - Ok with Self as inner type
139    /// 
140    /// * [CDnsResult] - Err with error description
141    pub 
142    fn make_a_aaaa_request<R>(resolvers: Arc<ResolveConfig>, req_name: R, opts: QuerySetup) -> QDns
143    where R: Into<QDnsName>
144    {
145        // store the A and AAAA depending on order
146        let reqs: Vec<QDnsReq> = 
147            match resolvers.family
148            {
149                ResolveConfigFamily::INET4_INET6 => 
150                {
151                    let req_n: QDnsName = req_name.into();
152
153                    vec![
154                        QDnsReq::new(req_n.clone(), QType::A),
155                        QDnsReq::new(req_n, QType::AAAA),
156                    ]
157                },
158                ResolveConfigFamily::INET6_INET4 => 
159                {
160                    let req_n: QDnsName = req_name.into();
161
162                    vec![
163                        QDnsReq::new(req_n.clone(), QType::AAAA),
164                        QDnsReq::new(req_n, QType::A),
165                    ]
166                },
167                ResolveConfigFamily::INET6 => 
168                {
169                    vec![
170                        QDnsReq::new(req_name.into(), QType::AAAA),
171                    ]
172                },
173                ResolveConfigFamily::INET4 => 
174                {
175                    vec![
176                        QDnsReq::new(req_name.into(), QType::A),
177                    ]
178                }
179                _ =>
180                {
181                    // set default
182                    let req_n: QDnsName = req_name.into();
183
184                    vec![
185                        QDnsReq::new(req_n.clone(), QType::A),
186                        QDnsReq::new(req_n, QType::AAAA),
187                    ]
188                }
189            };
190
191        
192        
193        return
194            Self
195            {
196                resolvers: resolvers,
197                ordered_req_list: reqs,
198                opts: opts,
199            };
200    }
201
202    /// Runs the created query/ies
203    /// 
204    /// # Returns
205    /// 
206    /// * [CDnsResult] with [QDnsQueriesRes] which may contain results
207    /// 
208    /// * [CDnsResult] with error
209    /// 
210    /// Should not panic. MT-safe.
211    pub async 
212    fn query(mut self) -> QDnsQueriesRes
213    {
214        // check if we need to measure time
215        let now = 
216            if self.opts.measure_time == true
217            {
218                Some(Instant::now())
219            }
220            else
221            {
222                None
223            };
224
225        let mut qres: QDnsQueriesRes = QDnsQueriesRes::DnsNotAvailable;
226
227        // determine where to look firstly i.e file -> bind, bind -> file
228        // or bind only, or file only
229        if self.resolvers.lookup.is_file_first()
230        {
231            match self.lookup_file(now.clone()).await
232            {
233                Ok(r) =>
234                    qres.extend(r),
235                Err(e) =>
236                    write_error!("{}", e),
237            }
238
239            // if something left unresolved, try ask internet
240            if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_bind() == true
241            {
242                match self.process_request(now.clone()).await
243                {
244                    Ok(r) => 
245                        qres.extend(r),
246                    Err(e) =>
247                        write_error!("{}", e),
248                }
249            }
250        }
251        else
252        {
253            match self.process_request(now.clone()).await
254            {
255                Ok(r) => 
256                    qres.extend(r),
257                Err(e) =>
258                    write_error!("{}", e),
259            }
260
261
262            if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_file() == true
263            {
264                match self.lookup_file(now.clone()).await
265                {
266                    Ok(r) => 
267                        qres.extend(r),
268                    Err(e) =>
269                        write_error!("{}", e),
270                }
271            }
272        }
273
274        return qres;
275    }
276
277    /// Returns timeout
278    fn get_timeout(&self) -> Duration
279    {
280        if let Some(timeout) = self.opts.timeout
281        {
282            return Duration::from_secs(timeout as u64);
283        }
284        else
285        {
286            return Duration::from_secs(self.resolvers.timeout as u64);
287        }
288    }
289
290    /// Searches in /etc/hosts
291    async 
292    fn lookup_file(&mut self, now: Option<Instant>) -> CDnsResult<QDnsQueriesRes>
293    {
294        // check if the it is overriden
295        if self.opts.ign_hosts == false
296        {
297            let hlist = CACHE.clone_host_list().await?;
298
299            let mut dnsquries: LinkedList<QDnsQuery> = LinkedList::new();
300
301            self.ordered_req_list.retain(|req| 
302                {
303                    match *req.get_type()
304                    {
305                        QType::A | QType::AAAA => 
306                        {
307                            let req_name = String::from(req.get_req_name());
308    
309                            if let Some(res) = hlist.search_by_fqdn(req.get_type(), req_name.as_str())
310                            {
311                                // create storage for response
312                                let drp = 
313                                    match DnsResponsePayload::new_local(*req.get_type(), res)
314                                    {
315                                        Ok(r) => r,
316                                        Err(e) =>
317                                        {
318                                            write_error!("{}", e);
319
320                                            return true;
321                                        }
322                                    };
323
324                                // store to list
325                                dnsquries.push_back(
326                                    QDnsQuery::from_local(drp, now.as_ref())
327                                );
328
329                                return false;
330                            }
331                            else
332                            {
333                                return true;
334                            }
335                        },
336                        QType::PTR => 
337                        {
338                            let ip: IpAddr = 
339                                match IpAddr::try_from(req.get_req_name())
340                                {
341                                    Ok(r) => r,
342                                    Err(e) =>
343                                    {
344                                        // just skip
345                                        write_error!("{}", e);
346
347                                        return true;
348                                    }
349                                };
350    
351                            if let Some(r) = hlist.search_by_ip(&ip)
352                            {   
353                                // create storage for response
354                                let drp = 
355                                    match DnsResponsePayload::new_local(QType::PTR, r)
356                                    {
357                                        Ok(r) => r,
358                                        Err(e) =>
359                                        {
360                                            write_error!("{}", e);
361
362                                            return true;
363                                        }
364                                    };
365
366                                // store to list
367                                dnsquries.push_back(
368                                    QDnsQuery::from_local(drp, now.as_ref())
369                                ); 
370
371                                return false;
372                            }
373                            else
374                            {
375                                return true;
376                            }
377                        },
378                        _ => 
379                        {
380                            // just skip, don't throw error
381                            return true;
382                        }
383                    }
384                }
385            );
386
387            /*for req in self.ordered_req_list.iter()
388            {   
389            }*/
390
391            return Ok( QDnsQueriesRes::from(dnsquries) );
392        }
393        else
394        {
395            return Ok(QDnsQueriesRes::DnsNotAvailable);
396        }
397    }
398
399    /// Creates socket based on config and flag. If `force_tcp` is set then Tcp tap will
400    /// be created. 
401    fn create_socket(
402        &self,
403        force_tcp: bool, 
404        resolver: &ResolveConfEntry, 
405        timeout: Duration
406    ) -> CDnsResult<Box<NetworkTapType>>
407    {
408        // create socket, if `force_tcp` is set then the attempt to switch from UDP to TCP
409        if self.resolvers.option_flags.is_force_tcp() == true || force_tcp == true
410        {
411            return new_tcp(resolver.get_resolver_ip(), 53, resolver.get_adapter_ip(), timeout);
412        }
413        else
414        {
415            return new_udp(resolver.get_resolver_ip(), 53, resolver.get_adapter_ip(), timeout);
416        };
417    }
418
419    /// Creates the sockets based on the config i.e if socket reopen is needed. 
420    fn create_sockets(
421        &self,
422        resolver: &ResolveConfEntry, 
423        requery_list: Option<LinkedList<DnsRequestHeader>>,
424        timeout: Duration,
425        force_tcp: bool,
426    ) -> CDnsResult<AsyncTaps>
427    {
428        let mut taps: AsyncTaps = AsyncTaps::new_with_capacity(self.ordered_req_list.len());
429
430        let force_tcp: bool = 
431            force_tcp == true || self.resolvers.option_flags.is_force_tcp() == true;
432
433        if let Some(requery) = requery_list
434        {
435            let mut ids: HashSet<u16> = HashSet::with_capacity(requery.len());
436            
437            for mut req in requery
438            {
439                if self.resolvers.option_flags.is_reopen_socket() == true || taps.len() == 0
440                { 
441                    // create socket and store
442                    let tap = 
443                        self.create_socket(force_tcp, resolver, timeout)?;
444
445                    // make sure the ID is uniq
446                    loop
447                    {
448                        req.regenerate_id();
449
450                        if ids.insert(req.get_id()) == true
451                        {
452                            break;
453                        }
454                    }
455
456                    let t = Tap::new(tap, req);
457
458                    taps.push(t);
459                }
460                else
461                {
462                    taps.push_to_last(req);
463                }
464            }
465        }
466        else
467        {
468            let mut ids: HashSet<u16> = HashSet::with_capacity(self.ordered_req_list.len());
469
470            // form the list of reqests
471            for req in self.ordered_req_list.iter()
472            {
473                let mut drh_req = DnsRequestHeader::from_qdns_req(req, self.resolvers.as_ref())?;
474
475                if self.resolvers.option_flags.is_reopen_socket() == true || taps.len() == 0
476                { 
477                    // create socket and store
478                    let tap = 
479                        self.create_socket(force_tcp, resolver, timeout)?;
480                    
481                    // make sure the ID is uniq
482                    loop
483                    {
484                        if ids.insert(drh_req.get_id()) == true
485                        {
486                            break;
487                        }
488
489                        drh_req.regenerate_id();
490                    }
491
492                    let t = Tap::new(tap, drh_req);
493
494                    taps.push(t);
495                }
496                else
497                {
498                    taps.push_to_last(drh_req);
499                }
500            } // for
501        }
502
503        return Ok(taps);
504    }
505
506    /// Returns the [QDnsQueryRec::Ok] only if no other type did not occure
507    fn get_result(responses: &LinkedList<QDnsQuery>) -> QDnsQueryRec
508    {
509        let mut resp = QDnsQueryRec::Ok;
510
511        for r in responses.iter()
512        {
513            if r.is_ok() == false
514            {
515                resp = r.status;
516
517                return resp;
518            }
519        }
520
521        return resp;
522    }
523
524    /// Accesses the first record in responses and gets the Authorative flag
525    fn get_authorative(responses: &LinkedList<QDnsQuery>) -> bool
526    {
527        return responses.front().map_or(false, |r| r.aa);
528    }
529
530    /// Quering all nameservers
531    async 
532    fn process_request(&mut self, now: Option<Instant>) -> CDnsResult<QDnsQueriesRes>
533    {
534        let mut qresponses: LinkedList<QDnsQuery> = LinkedList::new();
535
536       // let mut resolved: HashSet<&'req QDnsReq> = HashSet::with_capacity(self.ordered_req_list.len());
537
538        for resolver in self.resolvers.get_resolvers_iter()
539        {
540            let qresp = 
541                self.processing(now, resolver, None, false).await?;
542            
543            // check if results is ok
544            let dnsres = Self::get_result(&qresp);
545            let aa = Self::get_authorative(&qresp);
546
547            qresponses.extend(qresp);
548
549            if dnsres.try_next_nameserver(aa) == false
550            {
551                break;
552            }
553            
554        }
555
556        return Ok(QDnsQueriesRes::from(qresponses));
557    }
558
559    /// Processes request/s for nameserver
560    #[async_recursion]
561    async 
562    fn processing(
563        &self, 
564        now: Option<Instant>, 
565        resolver: &ResolveConfEntry,
566        requery: Option<LinkedList<DnsRequestHeader>>,
567        force_tcp: bool
568    ) -> CDnsResult<LinkedList<QDnsQuery>>
569    {
570        let mut responses: LinkedList<QDnsQuery> = LinkedList::new();
571
572        // a storage fot truncated request
573        let mut truncated_list: LinkedList<DnsRequestHeader> = LinkedList::new();
574
575        // form the list of reqests binded to taps
576        let sync_taps: AsyncTaps = 
577            self.create_sockets(resolver, requery, self.get_timeout(), force_tcp)?;
578
579        // a list of JoinHandles
580        let mut handles: Vec<JoinHandle<CDnsResult<Vec<SpawnFutereRes>>>> = Vec::with_capacity(sync_taps.len());
581
582        for stap in sync_taps.into_iter()
583        {
584            // get socket
585            //let socktap = stap.clone_tap();
586            // generate uniq ID for reqeust
587            //let req_list = stap.inner_requests();
588
589            let (mut socktap, req_list) = stap.into_inner();
590
591            if self.resolvers.option_flags.is_no_parallel() == true
592            {
593                // --- no parallel ----
594                // connect socket
595                socktap.connect().await?;
596
597                for req in req_list
598                {
599                    let res = 
600                        Self::spawn_future_single(
601                            socktap.as_mut(),
602                            req, 
603                            now.map_or(None, |n| Some(n.clone()))
604                        ).await;
605
606                    match res
607                    {
608                        Ok(SpawnFutereRes::Ok(dq)) =>
609                        {
610                            responses.push_back(dq);
611                        },
612                        Ok(SpawnFutereRes::Truncated(drh)) =>
613                        {
614                            if force_tcp == true || self.resolvers.option_flags.is_force_tcp() == true
615                            {
616                                // throw error
617                                internal_error!(CDnsErrorType::MessageTruncated, "Message is truncated even using TCP. Give up.");
618                            }
619
620                            truncated_list.push_back(drh);
621                        },
622                        Err(e) => return Err(e),
623                    }
624                }
625            }
626            else
627            {
628                // spawn async job
629                let res: JoinHandle<CDnsResult<Vec<SpawnFutereRes>>> = 
630                    tokio::spawn( 
631                        async move { 
632                            return 
633                                Self::spawn_future_multi(
634                                    socktap, 
635                                    req_list, 
636                                    now.map_or(None, |n| Some(n.clone()))
637                                ).await; 
638                        } 
639                    );
640
641                // store in list
642                handles.push(res);
643            }
644        }
645
646        // if the mode is parallel then this section will be exeuted
647        for handle in handles
648        {
649
650            let res = 
651                handle.await
652                    .map_err(|e| 
653                        internal_error_map!(CDnsErrorType::InternalError, "{}", e)
654                    )?;
655            
656            let v_sfr = 
657                match res
658                {
659                    Ok(r) => r,
660                    Err(e) => return Err(e),
661                };
662
663            for sft in v_sfr
664            {
665                match sft
666                {
667                    SpawnFutereRes::Ok(dq) =>
668                    {
669                        responses.push_back(dq);
670                    },
671                    SpawnFutereRes::Truncated(drh) =>
672                    {
673                        if force_tcp == true || self.resolvers.option_flags.is_force_tcp() == true
674                        {
675                            // throw error
676                            internal_error!(CDnsErrorType::MessageTruncated, "Message is truncated even using TCP. Give up.");
677                        }
678
679                        truncated_list.push_back(drh);
680                    },
681                }
682            }
683        }
684
685        if truncated_list.is_empty() == false
686        {
687            // befor leaving we need to finish resolving what was truncated
688            if force_tcp == true || self.resolvers.option_flags.is_force_tcp() == true
689            {
690                // throw error
691                internal_error!(CDnsErrorType::MessageTruncated, "Message is truncated even using TCP. Give up.");
692            }
693
694            let res = 
695                self.processing(now, resolver, Some(truncated_list), true).await?;
696
697            responses.extend(res);
698        }
699        
700        return Ok(responses);
701    }
702
703    /// Makes single request to the nameserver. `sock` myst be connected.
704    async 
705    fn spawn_future_single(sock: &mut NetworkTapType, req: DnsRequestHeader, now: Option<Instant>) -> CDnsResult<SpawnFutereRes>
706    {
707        // construct packet
708        let pkt = req.async_to_bytes().await?;
709
710        // send over wire
711        sock.send(pkt.as_slice()).await?;
712
713        // receive
714        let mut rcvbuf = vec![0_u8; 1024];
715
716        // receive message
717        sock.recv(rcvbuf.as_mut_slice()).await?;
718
719        // parsing response to structure
720        let ans = DnsRequestAnswer::async_try_from(rcvbuf.as_slice()).await?;
721
722        // verify the request with response
723        match ans.verify(&req)
724        {
725            Ok(_) => {},
726            Err(ref e) 
727                if e.err_code == CDnsErrorType::MessageTruncated =>
728            {
729                // message was received truncated, then add to list
730                // to resend query via TCP if not TCP
731                async_write_error!("{}", e);
732
733                return Ok(SpawnFutereRes::Truncated(req));
734            },
735            Err(e) => 
736                return Err(e),
737        }
738
739        // verified
740        let resp = QDnsQuery::from_response(sock.get_remote_addr(), ans, now.as_ref())?;
741
742        return Ok(SpawnFutereRes::Ok(resp));
743    }
744
745    async 
746    fn spawn_future_multi(mut sock: Box<NetworkTapType>, req_list: Vec<DnsRequestHeader>, now: Option<Instant>) -> CDnsResult<Vec<SpawnFutereRes>>
747    {
748        let mut spawn_res: Vec<SpawnFutereRes> = Vec::with_capacity(req_list.len());
749
750        // connect socket
751        sock.connect().await?;
752
753        for req in req_list
754        {
755            let r = Self::spawn_future_single(sock.as_mut(), req, now.clone()).await?;
756
757            spawn_res.push(r);
758        }
759
760        return Ok(spawn_res);
761    }
762}
763
764#[cfg(test)]
765mod tests
766{
767    use std::net::IpAddr;
768
769    use crate::{a_sync::{caches::CACHE, common::{byte2hexchar, ip2pkt}, QDns, QDnsName}, DnsRdata, QDnsQueriesRes, QDnsQueryRec, QType, QuerySetup};
770
771    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
772    async fn test_ip2pkt()
773    {
774        use tokio::time::Instant;
775        use std::net::{IpAddr, Ipv4Addr};
776        
777        let test = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
778
779        let now = Instant::now();
780
781        let res = ip2pkt(&test);
782
783        let elapsed = now.elapsed();
784        println!("Elapsed: {:.2?}", elapsed);
785
786        assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
787
788        let res = res.unwrap();
789        let ctrl = b"\x01\x38\x01\x38\x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\x61\x00";
790
791        assert_eq!(res.as_slice(), ctrl);
792    }
793
794
795    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
796    async fn test_byte2hexchar()
797    {
798        assert_eq!(byte2hexchar(1), 0x31);
799        assert_eq!(byte2hexchar(9), 0x39);
800        assert_eq!(byte2hexchar(10), 'a' as u8);
801        assert_eq!(byte2hexchar(15), 'f' as u8);
802    }
803
804
805    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
806    async fn reverse_lookup_test()
807    {
808        use tokio::time::Instant;
809        
810        let ipp: IpAddr = "8.8.8.8".parse().unwrap();
811        let test = QDnsName::from(&ipp);
812
813        let resolvers = CACHE.clone_resolve_list().await.unwrap();
814
815        let mut query_setup = QuerySetup::default();
816        query_setup.set_measure_time(true);
817
818        let now = Instant::now();
819
820        let mut dns_req = 
821            QDns::make_empty(resolvers, 1, query_setup);
822
823        dns_req.add_request(QType::PTR, test);
824
825        let res = dns_req.query().await;
826
827        let elapsed = now.elapsed();
828        println!("Elapsed: {:.2?}", elapsed);
829
830        assert_eq!(res.is_results(), true);
831
832        println!("{}", res);
833
834        match res
835        {
836            QDnsQueriesRes::DnsOk{ res } =>
837            {
838                let rec = &res[0];
839
840                //assert_eq!(rec.server.as_str(), "/etc/hosts");
841                assert_eq!(rec.status, QDnsQueryRec::Ok);
842
843                assert_eq!(rec.resp.len(), 1);
844                assert_eq!(rec.resp[0].rdata, DnsRdata::PTR{ fqdn: "dns.google".to_string() });
845                
846            },
847            _ => assert_eq!(true, false, "expected DnsResultSingle"),
848        }
849    }
850
851    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
852    async fn reverse_lookup_hosts_test()
853    {
854        use tokio::time::Instant;
855
856        let ipp: IpAddr = "127.0.0.1".parse().unwrap();
857        let test = QDnsName::from(&ipp);
858        
859        let now = Instant::now();
860
861        let mut query_setup = QuerySetup::default();
862        query_setup.set_measure_time(true);
863
864        let resolvers = CACHE.clone_resolve_list().await.unwrap();
865
866        let mut dns_req = 
867            QDns::make_empty(resolvers, 1, query_setup);
868
869        dns_req.add_request(QType::PTR, test);
870
871        let res = dns_req.query().await;
872        
873        let elapsed = now.elapsed();
874        println!("Elapsed: {:.2?}", elapsed);
875
876        assert_eq!(res.is_results(), true);
877
878        println!("{}", res);
879
880        match res
881        {
882            QDnsQueriesRes::DnsOk{ res } =>
883            {
884                let rec = &res[0];
885
886                assert_eq!(rec.server.as_str(), "/etc/hosts");
887                assert_eq!(rec.status, QDnsQueryRec::Ok);
888
889                assert_eq!(rec.resp.len(), 1);
890                assert_eq!(rec.resp[0].rdata, DnsRdata::PTR{ fqdn: "localhost".to_string() });
891                
892            },
893            _ => assert_eq!(true, false, "expected DnsResultSingle"),
894        }
895    }
896
897
898    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
899    async fn reverse_lookup_a()
900    {
901        use tokio::time::Instant;
902
903        let test = QDnsName::from("dns.google");
904
905        let mut query_setup = QuerySetup::default();
906        query_setup.set_measure_time(true);
907
908
909        let resolvers = CACHE.clone_resolve_list().await.unwrap();
910        
911        let res = QDns::make_a_aaaa_request(resolvers, test, query_setup);
912        
913        
914        let now = Instant::now();
915        let res = res.query().await;
916        
917
918        let elapsed = now.elapsed();
919        println!("Elapsed: {:.2?}", elapsed);
920
921        println!("{}", res);
922    }
923}