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