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::{BTreeSet, HashMap};
25use std::convert::TryFrom;
26use std::io::ErrorKind;
27use std::net::{IpAddr, SocketAddr, TcpStream, ToSocketAddrs, UdpSocket};
28use std::vec;
29use std::sync::Arc;
30use std::time::Duration;
31use std::time::Instant;
32
33use crate::cfg_resolv_parser::{ConfigEntryTls, ResolveConfEntry, ResolveConfig, ResolveConfigFamily};
34use crate::{error::*, write_error};
35use crate::query::{QDnsQuery, QDnsQueryResult, QuerySetup};
36use crate::sync::network::NetworkTap;
37use crate::internal_error;
38use crate::query_private::QDnsReq;
39use crate::common::*;
40
41use super::caches::CACHE;
42use super::network::{SocketTap, TcpHttpsConnection, TcpTlsConnection};
43
44/// A struct which implements the [ToSocketAddrs] to resolve A and AAAA
45/// directly to functions which argumnets are implementing [ToSocketAddrs]
46/// Available only for the `sync` version. Tokio has ToSocketAddrs `sealed`.
47#[derive(Clone, Debug)]
48pub enum QDnsSockerAddr
49{
50    Ip(SocketAddr),
51    Host(String, u16)
52}
53
54impl ToSocketAddrs for QDnsSockerAddr
55{
56    type Iter = vec::IntoIter<SocketAddr>;
57
58    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> 
59    {
60        match self
61        {
62            QDnsSockerAddr::Ip(socket_addr) => 
63                return Ok(vec![socket_addr.clone()].into_iter()),
64            QDnsSockerAddr::Host(host, port) => 
65            {
66                let qdns = 
67                    QDns::make_a_aaaa_request(None, host.as_str(), QuerySetup::default())
68                        .map_err(|e|
69                            std::io::Error::new(ErrorKind::NotFound, CDnsSuperError::from(e))
70                        )?;
71
72                let res = 
73                    qdns
74                        .query()
75                        .collect_ok()
76                        .into_iter()
77                        .map(|resp|
78                            resp
79                                .get_responses()
80                                .iter()
81                                .map(|r| 
82                                    r.get_rdata().get_ip().map(|f| SocketAddr::new(f, *port))
83                                )
84                                .collect::<Vec<Option<SocketAddr>>>()
85                        )
86                        .flatten()
87                        .filter(|p| p.is_some())
88                        .map(|v| v.unwrap())
89                        .collect::<Vec<SocketAddr>>();
90
91                return Ok( res.into_iter() );
92            }
93        }
94    }
95}
96
97
98impl QDnsSockerAddr
99{
100    /// Creates instance for A and AAAA records resolution from configured
101    /// in `/etc/resolv.conf` DNS servers. The instance performs the DNS resolve
102    /// when passed to functions which arguments implements [ToSocketAddrs]. 
103    /// 
104    /// # Arguments 
105    /// 
106    /// * `host` - `domain`:`port` i.e example.com:443. The `ip``port` is also
107    ///     possible and no DNS resolution will be performed.
108    pub 
109    fn resolve<D>(host: D) -> std::io::Result<Self> 
110    where D: AsRef<str>
111    {
112        if let Ok(addr) = host.as_ref().parse::<SocketAddr>() 
113        {
114            return Ok(Self::Ip(addr));
115        }
116        else
117        {
118            let ref_host = host.as_ref();
119
120            let (domain, port) = 
121                match ref_host.split_once(":")
122                {
123                    Some((h, portno)) => 
124                    {
125                        let port: u16 = 
126                            portno
127                                .parse()
128                                .map_err(|e| 
129                                    std::io::Error::new(ErrorKind::InvalidData, format!("{}", e))
130                                )?;
131
132                        (h, port)
133                    },
134                    None => 
135                        return Err(std::io::Error::new(ErrorKind::InvalidData, "missing prot number"))
136                };
137            
138            return Ok(Self::Host(domain.to_string(), port));
139        }
140    }
141
142    /// Same as `resolve` but port is provided separatly.
143    ///     
144    /// # Arguments 
145    /// 
146    /// * `host` - `domain` name i.e example.com. The `ip`` is also
147    ///     possible and no DNS resolution will be performed.
148    /// 
149    /// * `port` - a port number to be added to the result.
150    pub 
151    fn resolve_port<D>(host: D, port: u16) -> std::io::Result<Self> 
152    where D: AsRef<str>
153    {
154        if let Ok(addr) = host.as_ref().parse::<IpAddr>() 
155        {
156            return Ok(Self::Ip(SocketAddr::new(addr, port)));
157        }
158        else
159        {   
160            return Ok(Self::Host(host.as_ref().to_string(), port));
161        }
162    }
163}
164
165/// A main instance which contains all common logic.
166#[derive(Clone, Debug)]
167pub struct QDns
168{
169    /// An instance of the parser /etc/resolv.conf or custom config
170    resolvers: Arc<ResolveConfig>,
171    /// A pre-ordered list of the requests, if more than one
172    ordered_req_list: Vec<QDnsReq>,
173    /// Override options
174    opts: QuerySetup,
175}
176
177impl QDns
178{
179    /// Initializes new empty storage for requests.
180    /// 
181    /// # Arguments
182    /// 
183    /// * `resolvers` - an [Arc] [ResolveConfig] which contains configuration i.e nameservers
184    /// 
185    /// * `opts` - [QuerySetup] additional options or overrides. Use default() for default
186    ///     values.
187    /// 
188    /// # Returns
189    /// 
190    /// Never panics. Returns Self.
191    pub 
192    fn make_empty(resolvers: Option<Arc<ResolveConfig>>, opts: QuerySetup) -> CDnsResult<Self>
193    {
194        return Ok(
195            Self
196            {
197                resolvers: resolvers.unwrap_or(CACHE.clone_resolve_list()?),
198                ordered_req_list: Vec::new(),
199                opts: opts,
200            }
201        );
202    }
203
204    /// Adds new request to previously created empty storage for request with [QDns::make_empty].
205    /// 
206    /// # Arguemnts
207    /// 
208    /// * `qtype` - a [QType] type of the request
209    /// 
210    /// * `req_name` - a [Into] [QDnsName] which is target. i.e 'localhost' or 'domain.tld'
211    /// 
212    /// # Returns
213    /// 
214    /// * [CDnsResult] - Ok with nothing in inner type
215    /// 
216    /// * [CDnsResult] - Err with error description
217    pub 
218    fn add_request(&mut self, qtype: QType, req_name: impl Into<QDnsName>)
219    {
220        let qr = QDnsReq::new_into(req_name, qtype);
221
222        self.ordered_req_list.push(qr);
223
224        return;
225    }
226
227    /// This is helper which makes for you an A, AAAA query. The order of A and AAAA and
228    /// which from both are allowed is defined in the [ResolveConfig].
229    /// 
230    /// Use this function directly. Do not use [QDns::make_empty]
231    /// 
232    /// # Arguments
233    /// 
234    /// * `resolvers` - an [Option] value [Arc] [ResolveConfig] which can be used to 
235    ///     override the system's `resolv.conf`
236    /// 
237    /// * `req_name` - a [Into] [QDnsName] which is target i.e 'localhost' or 'domain.tld'
238    /// 
239    /// * `opts` - [QuerySetup] additional options or overrides. Use default() for default
240    ///     values.
241    /// 
242    /// # Returns
243    /// 
244    /// A [CDnsResult] is returned;
245    /// 
246    /// * [Result::Ok] - with Self as inner type
247    /// 
248    /// * [Result::Err] - with error description
249    pub 
250    fn make_a_aaaa_request(resolvers_opt: Option<Arc<ResolveConfig>>, req_name: impl Into<QDnsName>, 
251        opts: QuerySetup) -> CDnsResult<Self>
252    {
253        let resolvers =  resolvers_opt.unwrap_or(CACHE.clone_resolve_list()?);
254
255        // store the A and AAAA depending on order
256        let reqs: Vec<QDnsReq> = 
257            match resolvers.family
258            {
259                ResolveConfigFamily::INET4_INET6 => 
260                {
261                    let req_n: QDnsName = req_name.into();
262
263                    vec![
264                        QDnsReq::new(req_n.clone(), QType::A),
265                        QDnsReq::new(req_n, QType::AAAA),
266                    ]
267                },
268                ResolveConfigFamily::INET6_INET4 => 
269                {
270                    let req_n: QDnsName = req_name.into();
271
272                    vec![
273                        QDnsReq::new(req_n.clone(), QType::AAAA),
274                        QDnsReq::new(req_n, QType::A),
275                    ]
276                },
277                ResolveConfigFamily::INET6 => 
278                {
279                    vec![
280                        QDnsReq::new(req_name.into(), QType::AAAA),
281                    ]
282                },
283                ResolveConfigFamily::INET4 => 
284                {
285                    vec![
286                        QDnsReq::new(req_name.into(), QType::A),
287                    ]
288                }
289                _ =>
290                {
291                    // set default
292                    let req_n: QDnsName = req_name.into();
293
294                    vec![
295                        QDnsReq::new(req_n.clone(), QType::A),
296                        QDnsReq::new(req_n, QType::AAAA),
297                    ]
298                }
299            };
300
301        
302        
303        return Ok(
304            Self
305            {
306                resolvers: resolvers,
307                ordered_req_list: reqs,
308                opts: opts,
309            }
310        );
311    }
312
313    /// Resolves the created query/ies.
314    /// 
315    /// # Returns
316    /// 
317    /// * [CDnsResult] with [QDnsQueryResult] which may contain results
318    /// 
319    /// * [CDnsResult] with error
320    /// 
321    /// Should not panic. MT-safe.
322    pub 
323    fn query(mut self) -> QDnsQueryResult
324    {
325        // check if we need to measure time
326        let now = 
327            if self.opts.measure_time == true
328            {
329                Some(Instant::now())
330            }
331            else
332            {
333                None
334            };
335
336        // determine where to look firstly i.e file -> bind, bind -> file
337        // or bind only, or file only
338        if self.resolvers.lookup.is_file_first()
339        {
340            let mut query_res = 
341                match self.lookup_file(now.as_ref())
342                {
343                    Ok(file) => 
344                    {
345                        if file.is_empty() == false
346                        {
347                            // remove records which was resolved
348                            self.ordered_req_list.retain(|req| 
349                                {
350                                    return !file.contains_dnsreq(req);
351                                }
352                            );
353                        }
354
355                        file
356                    },
357                    Err(e) => 
358                    {
359                        write_error!(e);
360
361                        QDnsQueryResult::default()
362                    }
363                };
364
365
366            // if something left unresolved, try ask internet
367            if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_bind() == true
368            {
369                let res = self.process_request(now.as_ref());
370                
371                query_res.extend(res);
372            }
373
374            return query_res;
375        }
376        else
377        {
378            let mut dns_res = self.process_request(now.as_ref());
379            if dns_res.is_empty() == false
380            {
381                // remove records which was resolved
382                self.ordered_req_list.retain(|req| 
383                    {
384                        return !dns_res.contains_dnsreq(req);
385                    }
386                );
387            }
388
389
390
391            if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_file() == true
392            {
393                match self.lookup_file(now.as_ref())
394                {
395                    Ok(res) => 
396                    {
397                        dns_res.extend(res);
398                    },
399                    Err(e) =>
400                    {
401                        write_error!(e);
402                    }
403                }
404            }
405
406            return dns_res;
407        }
408    }
409
410    /// Returns timeout
411    fn get_timeout(&self) -> Duration
412    {
413        if let Some(timeout) = self.opts.timeout
414        {
415            return Duration::from_secs(timeout as u64);
416        }
417        else
418        {
419            return Duration::from_secs(self.resolvers.timeout as u64);
420        }
421    }
422
423    /// Searches in /etc/hosts
424    fn lookup_file(&mut self, now: Option<&Instant>) -> CDnsResult<QDnsQueryResult>
425    {
426        let mut dnsquries: QDnsQueryResult = QDnsQueryResult::default();
427
428        // check if the it is overriden
429        if self.opts.ign_hosts == false
430        {
431            let hlist = CACHE.clone_host_list()?;
432
433            for req in self.ordered_req_list.iter()
434            {
435                match *req.get_type()
436                {
437                    QType::A | QType::AAAA => 
438                    {
439                        let req_name = String::from(req.get_req_name());
440
441                        let Some(host_name_ent) = hlist.search_by_fqdn(req.get_type(), req_name.as_str())
442                            else { continue };
443                        
444                        let Some(drp) = DnsResponsePayload::new_local(*req.get_type(), host_name_ent)
445                            else { continue };
446                        
447                        // store to list
448                        dnsquries.push(req.clone(), Ok(QDnsQuery::from_local(drp, now)));
449                    },
450                    QType::PTR => 
451                    {
452                        let Ok(ip) = IpAddr::try_from(req.get_req_name())
453                            else { continue };
454
455                        let Some(host_name_ent) = hlist.search_by_ip(&ip)
456                            else { continue };
457
458                        let Some(drp) = DnsResponsePayload::new_local(*req.get_type(), host_name_ent)
459                            else { continue };    
460
461                        dnsquries.push(req.clone(), Ok(QDnsQuery::from_local(drp, now))); 
462                    },
463                    _ => 
464                        continue,
465                }
466            }
467
468            
469        }
470
471        return Ok(dnsquries);
472    }
473
474    /// Creates socket based on config and flag. If `force_tcp` is set then Tcp tap will
475    /// be created.
476    #[inline]
477    fn create_socket(&self, force_tcp: bool, resolver: Arc<ResolveConfEntry>) -> CDnsResult<Box<dyn SocketTap>>
478    {
479        let is_tls = resolver.get_tls_type();
480
481        if is_tls == ConfigEntryTls::Tls
482        {
483            return NetworkTap::<TcpTlsConnection>::new(resolver, self.get_timeout());
484        }
485        else if is_tls == ConfigEntryTls::Https
486        {
487            return NetworkTap::<TcpHttpsConnection>::new(resolver, self.get_timeout());
488        }
489        else if self.resolvers.option_flags.is_force_tcp() == true || force_tcp == true
490        {
491            return NetworkTap::<TcpStream>::new(resolver, self.get_timeout());
492        }
493        else
494        {
495            return NetworkTap::<UdpSocket>::new(resolver, self.get_timeout());
496        }
497    }
498
499    /// Quering nameservers
500    fn process_request(&mut self, now: Option<&Instant>) -> QDnsQueryResult
501    {
502        let mut responses: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
503
504        if self.resolvers.option_flags.is_no_parallel() == true
505        {
506            for req in self.ordered_req_list.iter()
507            {
508                let mut last_resp: Option<CDnsResult<QDnsQuery>> = None;
509
510                for resolver in self.resolvers.get_resolvers_iter()
511                {
512                    match self.query_exec_seq(now, resolver.clone(), req, None)
513                    {
514                        Ok(resp) =>
515                        {
516                            if resp.should_check_next_ns() == true
517                            {
518                                last_resp = Some(Ok(resp));
519
520                                continue;
521                            }
522                            else
523                            {
524                                responses.push(req.clone(), Ok(resp));
525
526                                let _ = last_resp.take();
527
528                                break;
529                            }
530                        },
531                        Err(e) =>
532                        {
533                            if last_resp.is_none() == true
534                            {
535                                last_resp = Some(Err(e));
536                            }                      
537
538                            continue;
539                        }
540                    }
541                } // for
542
543                responses.push(req.clone(), last_resp.take().unwrap());
544            }// for
545        }
546        else
547        {
548           // let mut responses: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
549
550            for resolver in self.resolvers.get_resolvers_iter()
551            {
552                if self.ordered_req_list.is_empty() == true
553                {
554                    break;
555                }
556
557                match self.query_exec_pipelined(now, resolver.clone(), None)
558                {
559                    Ok(resp) =>
560                    {
561                        for (qdns_res, qdns_que) in resp
562                        {
563                            if let Ok(ref resp) = qdns_que
564                            {
565                                if resp.should_check_next_ns() == false
566                                    {
567                                        self
568                                            .ordered_req_list
569                                            .retain(
570                                                |req_item|
571                                                req_item != &qdns_res
572                                            );
573                                    }
574                            }
575                            
576                            responses.push(qdns_res, qdns_que);
577                        }
578                    },
579                    Err(e) =>
580                    {
581                        write_error!(e);
582
583                        continue;
584                    }
585                }
586            }
587        }
588
589        return responses;
590    }
591
592
593    fn query_exec_pipelined(
594        &self, 
595        now: Option<&Instant>, 
596        resolver: Arc<ResolveConfEntry>,
597        requery: Option<HashMap<DnsRequestHeader, QDnsReq>>,
598    ) -> CDnsResult<QDnsQueryResult>
599    {
600        let force_tcp = self.resolvers.option_flags.is_force_tcp() || requery.is_some();
601
602        let mut query_headers: HashMap<DnsRequestHeader, QDnsReq> = 
603            if let Some(requer) = requery
604            {
605                let pkts_ids = requer.iter().map(|q| q.0.get_id()).collect::<BTreeSet<u16>>();
606
607                // regenerate que ID and collect
608                requer
609                    .into_iter()
610                    .map(
611                        |(mut qrr, qdr)| 
612                        { 
613                            loop
614                            {
615                                qrr.regenerate_id(); 
616
617                                if pkts_ids.contains(&qrr.get_id()) == false
618                                {
619                                    break;
620                                }
621                            }
622                            (qrr, qdr)
623                        })
624                    .collect::<HashMap<DnsRequestHeader, QDnsReq>>()
625            }
626            else    
627            {
628                let mut pkts_ids: BTreeSet<u16> = BTreeSet::new();
629
630                // create que
631                self
632                    .ordered_req_list
633                    .iter()
634                    .map(
635                        |query| 
636                        {
637                            
638                            let mut drh_res = DnsRequestHeader::try_from(query);
639                            
640                            loop
641                            {
642                                if let Ok(ref mut drh) = drh_res
643                                {
644                                    if pkts_ids.contains(&drh.get_id()) == true
645                                    {
646                                        drh.regenerate_id();
647
648                                        continue;
649                                    }
650                                    else
651                                    {
652                                        pkts_ids.insert(drh.get_id());
653                                        break;
654                                    }
655                                }
656                                else
657                                {
658                                    break;
659                                }
660                            }
661
662                            drh_res.map(|dh| (dh, query.clone()))
663                        }
664                    )
665                    .collect::<CDnsResult<HashMap<DnsRequestHeader, QDnsReq>>>()?
666            };
667
668        // create socket and store
669        let mut tap = self.create_socket(force_tcp, resolver.clone())?;
670
671        tap.connect(true, CDdnsGlobals::get_tcp_conn_timeout())?;
672
673        // send everything
674        for qh in query_headers.iter()
675        {
676            let pkt = qh.0.to_bytes(tap.should_append_len())?;
677
678            tap.send(pkt.as_slice())?;
679        }
680
681        let mut resp: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
682        let mut requery: HashMap<DnsRequestHeader, QDnsReq> = HashMap::new();
683        // poll socket
684        
685        loop
686        {
687            if query_headers.len() == 0
688            {
689                break;
690            }
691            
692            /*let mut poll_tap = [PollFd::new(tap.get_fd().unwrap(), PollFlags::from(PollFlags::POLLIN))];
693            
694            let poll_res = 
695                nix::poll::poll(&mut poll_tap, self.resolvers.timeout * 1000 as u16)
696                    .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
697
698            if poll_res == 0
699            {
700                internal_error!(CDnsErrorType::IoError, "Timeout");
701            }
702
703            let Some(revents) = poll_tap[0].revents()
704                else
705                {
706                    internal_error!(CDnsErrorType::IoError, "Crate NIX returned None due to unknown data returned by kernel!\
707                        Try update nix crate! Probably it is outdated!");
708                };
709
710            if revents.intersects(PollFlags::POLLERR | PollFlags::POLLHUP | PollFlags::POLLNVAL ) == true
711            {
712                write_error!(internal_error_map!(CDnsErrorType::IoError, "socket poll error {:?}", revents));
713                break;
714            }
715            else if revents.intersects(PollFlags::POLLIN) == true
716            {*/
717                if tap.poll_read()? == false
718                {
719                    // timeout
720                    break;
721                }
722
723                let ans = tap.recv()?;//self.read_response(tap.as_mut())?;
724
725                let Some((query_header, qdnsreq)) = query_headers.remove_entry(&ans.req_header)
726                    else
727                    {
728                        internal_error!(CDnsErrorType::IoError, "can not find response with request: {}", ans.req_header);
729                    };
730
731                ans.verify(&query_header)?;
732
733                // verified
734                let qdns_resp = QDnsQuery::from_response(tap.get_remote_addr(), ans, now);
735
736                if let Ok(ref qdns) = qdns_resp
737                {
738                    if qdns.get_status().should_try_tcp() == true && force_tcp == false
739                    {
740                        requery.insert(query_header, qdnsreq);
741                    }
742                    else
743                    {
744                        resp.push(qdnsreq, qdns_resp);
745                    }
746                }
747                else
748                {
749                    resp.push(qdnsreq, qdns_resp);
750                }
751           // }
752        }
753
754        if requery.is_empty() == false
755        {
756            let res = self.query_exec_pipelined(now, resolver, Some(requery))?;
757
758            resp.extend(res);
759        }
760        
761        return Ok(resp);   
762    }
763
764    /// Runs sequentually
765    fn query_exec_seq(
766        &self, 
767        now: Option<&Instant>, 
768        resolver: Arc<ResolveConfEntry>,
769        query: &QDnsReq,
770        requery: Option<DnsRequestHeader>,
771    ) -> CDnsResult<QDnsQuery>
772    {
773        let force_tcp = self.resolvers.option_flags.is_force_tcp() || requery.is_some();
774
775        // create socket and store
776        let mut tap = self.create_socket(force_tcp, resolver.clone())?;
777
778        let query_header = 
779            // form the list of reqests binded to taps
780            if let Some(mut requery) = requery
781            {
782                requery.regenerate_id();
783
784                requery
785            }
786            else
787            {
788                let drh_req = DnsRequestHeader::try_from(query)?;
789                
790                drh_req
791            };
792
793        let res = 
794            {
795                // open connection, with timeout because working in non parallel mode
796                tap.connect(false, CDdnsGlobals::get_tcp_conn_timeout())?;
797
798                // convert to byte packet (the error returned is global error i.e OOM)
799                let pkt = query_header.to_bytes(tap.should_append_len())?;
800
801                // send to DNS server, if error happens, then stop processing this server
802                tap.send(pkt.as_slice())?;
803
804                let ans = tap.recv()?; //self.read_response(tap.as_mut())?;
805
806                ans.verify(&query_header)?;
807
808                // verified
809                let resp = QDnsQuery::from_response(tap.get_remote_addr(), ans, now)?;
810
811                Ok(resp)
812            };
813
814        if (res.is_ok() == true && res.as_ref().unwrap().status.should_try_tcp() == false) || 
815            (res.is_err() == true && force_tcp == true)
816        {
817            return res;
818        }
819        
820
821        return 
822            self.query_exec_seq(now, resolver.clone(), query, Some(query_header));
823    }
824
825    /*
826    // Reads response from tap
827    fn read_response(&self, socktap: &mut (dyn SocketTap)) -> CDnsResult<DnsRequestAnswer>
828    {
829        if socktap.should_append_len() == false && socktap.is_encrypted() == false
830        {
831            let mut rcvbuf = vec![0_u8; 1457];
832
833            // receive message
834            let n = socktap.recv(rcvbuf.as_mut_slice())?;
835
836            // parsing response to structure
837            return DnsRequestAnswer::parse(&rcvbuf, n); //DnsRequestAnswer::try_from(rcvbuf.as_slice(), n, socktap.is_tcp())?;
838        }
839        else if socktap.should_append_len() == false && socktap.is_encrypted() == true
840        {
841            let mut rcvbuf = vec![0_u8; 1457];
842        }
843        else
844        {
845            let mut pkg_pen: [u8; 2] = [0, 0];
846            let n = socktap.recv(&mut pkg_pen)?;
847
848            if n == 0
849            {
850                internal_error!(CDnsErrorType::IoError, "tcp received zero len message!");
851            }
852            else if n != 2
853            {
854                internal_error!(CDnsErrorType::IoError, "tcp expected 2 bytes to be read!");
855            }
856
857            let ln = u16::from_be_bytes(pkg_pen);
858
859            let mut rcvbuf = vec![0_u8; ln as usize];
860
861            // receive message
862            let mut n = socktap.recv(rcvbuf.as_mut_slice())?;
863
864            if n == 0
865            {
866                internal_error!(CDnsErrorType::IoError, "tcp received zero len message!");
867            }
868            else if n == 1
869            {
870                n = socktap.recv(&mut rcvbuf[1..])?;
871
872                if n == 0
873                {
874                    internal_error!(CDnsErrorType::IoError, "tcp received zero len message again!");
875                }
876
877                n += 1;
878            }
879
880            return DnsRequestAnswer::parse(&rcvbuf, n); 
881        }
882    }*/
883}
884
885#[cfg(test)]
886
887mod tests
888{
889    use std::net::IpAddr;
890
891    use crate::{common::{byte2hexchar, ip2pkt, RecordPTR, RecordReader}, sync::{query::QDns, QDnsName}, QDnsQueryRec, QType, QuerySetup};
892
893    #[test]
894    fn test_ip2pkt()
895    {
896        use std::time::Instant;
897        use std::net::{IpAddr, Ipv4Addr};
898        
899        let test = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
900
901        let now = Instant::now();
902
903        let res = ip2pkt(&test);
904
905        let elapsed = now.elapsed();
906        println!("Elapsed: {:.2?}", elapsed);
907
908        assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
909
910        let res = res.unwrap();
911        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";
912
913        assert_eq!(res.as_slice(), ctrl);
914    }
915
916
917    #[test]
918    fn test_byte2hexchar()
919    {
920        assert_eq!(byte2hexchar(1), 0x31);
921        assert_eq!(byte2hexchar(9), 0x39);
922        assert_eq!(byte2hexchar(10), 'a' as u8);
923        assert_eq!(byte2hexchar(15), 'f' as u8);
924    }
925
926    #[test]
927    fn reverse_lookup_test()
928    {
929        use std::time::Instant;
930        
931        let ipp: IpAddr = "8.8.8.8".parse().unwrap();
932        //let test = QDnsName::try_from(&ipp).unwrap();
933
934        let mut query_setup = QuerySetup::default();
935        query_setup.set_measure_time(true);
936
937        let now = Instant::now();
938
939        let mut dns_req = 
940            QDns::make_empty(None, query_setup).unwrap();
941
942        dns_req.add_request(QType::PTR, ipp);
943        let res = dns_req.query();
944
945        let elapsed = now.elapsed();
946        println!("Elapsed: {:.2?}", elapsed);
947
948        println!("{}", res);
949
950        assert_eq!(res.is_empty(), false);
951
952        let recs = res.collect_ok();
953        let rec = &recs[0];
954        //assert_eq!(rec.server.as_str(), "/etc/hosts");
955        assert_eq!(rec.status, QDnsQueryRec::Ok);
956
957        assert_eq!(rec.resp.len(), 1);
958        assert_eq!(rec.resp[0].rdata, RecordPTR::wrap(RecordPTR{ fqdn: "dns.google".to_string() }));
959    }
960
961    #[test]
962    fn reverse_lookup_hosts_test()
963    {
964        use std::time::Instant;
965
966        let ipp: IpAddr = "127.0.0.1".parse().unwrap();
967        //let test = QDnsName::try_from(&ipp).unwrap();
968        
969        let now = Instant::now();
970
971        let mut query_setup = QuerySetup::default();
972        query_setup.set_measure_time(true);
973
974        let mut dns_req = 
975            QDns::make_empty(None, query_setup).unwrap();
976
977        dns_req.add_request(QType::PTR, ipp);
978
979        let res = dns_req.query();
980        
981        let elapsed = now.elapsed();
982        println!("Elapsed: {:.2?}", elapsed);
983
984        println!("{}", res);
985
986        assert_eq!(res.is_empty(), false);
987
988        let recs = res.collect_ok();
989        let rec = &recs[0];
990
991        assert_eq!(rec.server.as_str(), "/etc/hosts");
992        assert_eq!(rec.status, QDnsQueryRec::Ok);
993
994        assert_eq!(rec.resp.len(), 1);
995        assert_eq!(rec.resp[0].rdata, RecordPTR::wrap(RecordPTR{ fqdn: "localhost".to_string() }));
996    }
997
998
999    #[test]
1000    fn reverse_lookup_a()
1001    {
1002        use std::time::Instant;
1003
1004       // let test = QDnsName::try_from("dns.google").unwrap();
1005
1006        let mut query_setup = QuerySetup::default();
1007        query_setup.set_measure_time(true);
1008
1009
1010        let res = 
1011            QDns::make_a_aaaa_request(None, "dns.google", query_setup).unwrap();
1012        
1013        
1014        let now = Instant::now();
1015        let res = res.query();
1016        
1017
1018        let elapsed = now.elapsed();
1019        println!("Elapsed: {:.2?}", elapsed);
1020
1021    /*  assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1022        
1023        let res = res.unwrap();*/
1024        println!("{}", res);
1025    }
1026
1027
1028    #[test]
1029    fn truncation_test_1()
1030    {
1031        use std::time::Instant;
1032
1033        //let test = QDnsName::try_from("truncate-zyxw11.go.dnscheck.tools").unwrap();
1034
1035        let mut query_setup = QuerySetup::default();
1036        query_setup.set_measure_time(true);
1037
1038
1039        let mut res = 
1040            QDns::make_empty(None, query_setup).unwrap();
1041
1042        res.add_request(QType::TXT, "truncate-zyxw11.go.dnscheck.tools");
1043        
1044        
1045        let now = Instant::now();
1046        let res = res.query();
1047        
1048
1049        let elapsed = now.elapsed();
1050        println!("Elapsed: {:.2?}", elapsed);
1051
1052    /*  assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1053        
1054        let res = res.unwrap();*/
1055        println!("{}", res);
1056    }
1057
1058    #[test]
1059    fn truncation_test_2()
1060    {
1061        use std::time::Instant;
1062
1063       // let test = QDnsName::try_from("dnscheck.tools").unwrap();
1064      //  let test2 = QDnsName::try_from("localhost").unwrap();
1065      //  let test3 = QDnsName::try_from("unknownnonexistentdomain.com").unwrap();
1066      //  let test4 = QDnsName::try_from("example.com").unwrap();
1067
1068        let mut query_setup = QuerySetup::default();
1069        query_setup.set_measure_time(true);
1070
1071
1072        let mut res = 
1073            QDns::make_empty(None, query_setup).unwrap();
1074
1075        res.add_request(QType::A, "dnscheck.tools");
1076        res.add_request(QType::A, "localhost");
1077        res.add_request(QType::A, "unknownnonexistentdomain.com");
1078        res.add_request(QType::TXT, "example.com");
1079        
1080        
1081        let now = Instant::now();
1082        let res = res.query();
1083        
1084
1085        let elapsed = now.elapsed();
1086        println!("Elapsed: {:.2?}", elapsed);
1087
1088
1089        let (ans, errs) = res.collect_split();
1090
1091        println!("ANSWERS");
1092        for a in ans
1093        {
1094            println!("-----\n{} \n{}", a.0, a.1.unwrap());
1095        }
1096
1097        println!("ERRORS");
1098        for b in errs
1099        {
1100            println!("-----\n{} \n{}", b.0, b.1.err().unwrap());
1101        }
1102    }
1103}