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