cdns_rs/a_sync/
query.rs

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