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