cdns_rs/
cfg_resolv_parser.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::fmt;
25/// This file contains the config file parsers.
26
27
28use std::hash::Hash;
29use std::net::{IpAddr, SocketAddr};
30use std::str::FromStr;
31use std::sync::atomic::{AtomicUsize, Ordering};
32use std::sync::Arc;
33
34use bitflags::bitflags;
35
36use crate::common::{bind_all, DEF_HTTPS_PORT, DEF_TLS_PORT};
37use crate::{error::*, internal_error, internal_error_map, write_error};
38use crate::portable::*;
39use crate::tokenizer::*;
40
41/// A rounrobin counter. It was intentionally designed to count forward as
42/// it is not expected that the counter will ever be overflowed.
43static ROUND_ROBIN_CNT: AtomicUsize = AtomicUsize::new(0);
44
45
46
47/// An implementaion of RoundRobin iterator. This iterator is sharing a single
48/// source of counting to implement roundrobin functionality.
49pub struct RoundRobinIterator<'iterator, I>
50{
51    end_index: usize,
52    current_index: usize,
53    slice: &'iterator [I]
54}
55
56impl<'iterator, I> RoundRobinIterator<'iterator, I>
57{
58    pub 
59    fn new(start_index: usize, slice: &'iterator [I]) -> RoundRobinIterator<'iterator, I>
60    {        
61        return 
62            RoundRobinIterator
63            {
64                end_index: start_index + slice.len(),
65                current_index: start_index,
66                slice: slice
67            };
68    }
69
70    pub 
71    fn len(&self) -> usize
72    {
73        return self.slice.len();
74    }
75} 
76
77impl<'iterator, I> Iterator for RoundRobinIterator<'iterator, I>
78{
79    type Item = &'iterator I;
80
81    fn next(&mut self) -> Option<Self::Item> 
82    {
83        let temp_index = self.current_index;
84        
85        self.current_index += 1;
86
87        if temp_index == self.end_index
88        {
89            return None;
90        }
91
92        // compute real index
93        let real_index = temp_index % self.slice.len();
94
95        return self.slice.get(real_index);
96    }
97}
98
99#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub enum ConfigEntryTls
101{
102    None, Tls, Https
103}
104
105impl fmt::Display for ConfigEntryTls
106{
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
108    {
109        match self
110        {
111            Self::None => write!(f, "unencrypted"),
112            Self::Tls => write!(f, "TLS"),
113            Self::Https => write!(f, "HTTPS"),
114        }
115    }
116}
117
118/// A resolve.conf parser
119#[derive(Clone, Debug, PartialEq, Eq)]
120pub struct ResolveConfEntry 
121{
122    ip: SocketAddr,
123    tls_domain: Option<String>,
124    https_req_path: Option<String>,
125    adapter_ip: SocketAddr,
126    tls_type: ConfigEntryTls,
127}
128
129impl ResolveConfEntry
130{
131    pub 
132    fn new(ip: SocketAddr, tls_url: Option<String>, adapter_ip: SocketAddr) -> CDnsResult<Self>
133    {
134        let (tls_type, https_domain, https_req_path) = 
135            if let Some(tlsd) = tls_url.as_ref()
136            {
137                if let Some(domain_path_https) = tlsd.strip_prefix("https://")
138                {
139                    let Some ((domain, path)) = domain_path_https.split_once("/")
140                        else 
141                        {
142                            internal_error!(CDnsErrorType::ConfigError, 
143                                "in /etc/resolv.conf - invalid TLS/HTTPS URL '{}'", 
144                                domain_path_https
145                            )
146                        };
147
148                    (ConfigEntryTls::Https, Some(domain.to_string()), Some(path.to_string()))
149                }
150                else
151                {
152                    // TLS
153                    let domain = 
154                        tlsd
155                            .split_once("://")
156                            .map_or(tlsd.as_str(), |(_, d)| d);
157
158
159                    let domain = 
160                        domain
161                            .split_once("/")
162                            .map_or(domain, |(d, _)| d);
163
164                    
165                    (ConfigEntryTls::Tls, Some(domain.to_string()), None)
166                }
167            }
168            else
169            {
170                if ip.port() == DEF_HTTPS_PORT 
171                {
172                    (ConfigEntryTls::Https, None, None)
173                }
174                else if ip.port() == DEF_TLS_PORT
175                {
176                    (ConfigEntryTls::Tls, None, None)
177                }
178                else
179                {
180                    (ConfigEntryTls::None, None, None)
181                }
182            };
183
184        return Ok(Self {ip, tls_domain: https_domain, https_req_path: https_req_path, adapter_ip, tls_type});
185    }
186
187    pub 
188    fn get_resolver_ip(&self) -> IpAddr
189    {
190        return self.ip.ip();
191    }
192
193    pub 
194    fn get_resolver_sa(&self) -> &SocketAddr
195    {
196        return &self.ip;
197    }
198
199    pub 
200    fn get_tls_domain(&self) -> Option<&String>
201    {
202        return self.tls_domain.as_ref();
203    }
204
205    pub 
206    fn get_adapter_ip(&self) -> &SocketAddr
207    {
208        return &self.adapter_ip;
209    }
210
211    pub 
212    fn get_tls_type(&self) -> ConfigEntryTls
213    {
214        return self.tls_type;
215    }
216
217    pub 
218    fn get_tls_path(&self) -> Option<&String>
219    {
220        return self.https_req_path.as_ref();
221    }
222}
223
224/*
225#[derive(Clone, Debug, PartialEq, Eq)]
226pub enum ResolveConfigLookup
227{
228    File, Bind
229}*/
230
231bitflags! {
232    /// An 'option' flags which can be set in /etc/resolv.conf
233    #[derive(Clone, Debug, PartialEq, Eq, Copy)]
234    pub struct ResolveConfigLookup: u32 
235    {
236        /// Sets the bit order flag that first is FILE
237        const FILE_BIND  = 0x10;
238        /// Sets the bit order flag that first is BIND
239        const BIND_FILE  = 0x20;
240        /// FILE is enabled
241        const FILE       = 0x01;
242        /// BIND is enabled
243        const BIND       = 0x02;
244    }
245}
246
247
248impl ResolveConfigLookup
249{
250    pub 
251    fn is_file(&self) -> bool
252    {
253        return self.contains(Self::FILE);
254    }
255
256    pub 
257    fn is_bind(&self) -> bool
258    {
259        return self.contains(Self::BIND);
260    }
261
262    pub 
263    fn is_file_first(&self) -> bool
264    {
265        return self.contains(Self::FILE_BIND);
266    }
267}
268
269impl Default for ResolveConfigLookup
270{
271    fn default() -> Self 
272    {
273        return Self::FILE_BIND | Self::FILE | Self::BIND;
274    }
275}
276
277const LOOKUP_FILE: &'static str = "file";
278const LOOKUP_BIND: &'static str = "bind";
279
280impl TryFrom<&[String]> for ResolveConfigLookup
281{
282    type Error = CDnsError;
283
284    fn try_from(value: &[String]) -> Result<Self, Self::Error> 
285    {
286        if value.is_empty() == true
287        {
288            internal_error!(CDnsErrorType::ConfigError, "'lookup' is empty");
289        }
290        else if value.len() > 2
291        {
292            internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with too many paramenters: '{:?}'", value);
293        }
294
295        //let lookup_flags: Self = Self::empty();
296
297        let first = 
298            if value[0] == LOOKUP_FILE
299            {
300                Self::FILE
301            }
302            else if value[0] == LOOKUP_BIND
303            {
304                Self::BIND
305            }
306            else
307            {
308                internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with unknown parameter: '{}'", value[0]);
309            };
310
311        let second: Self = 
312            if value.len() == 2
313            {
314                if value[1] == LOOKUP_FILE
315                {
316                    if first.contains(Self::FILE) == true
317                    {
318                        internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with duplicate parameter: '{}'", value[1]);
319                    }
320                    
321                    Self::BIND_FILE | Self::FILE
322                }
323                else if value[1] == LOOKUP_BIND
324                {
325                    if first.contains(Self::BIND) == true
326                    {
327                        internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with duplicate parameter: '{}'", value[1]);
328                    }
329                    
330                    Self::FILE_BIND | Self::BIND
331                }
332                else
333                {
334                    internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with unknown parameter: '{}'", value[0]);
335                }
336            }
337            else
338            {
339                Self::empty()
340            };
341
342        return Ok(Self::from(first | second));
343        
344    }
345}
346
347bitflags! {
348    /// An 'option' flags which can be set in /etc/resolv.conf
349    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
350    pub struct ResolveConfigFamily: u32 
351    {
352        /// Sets the bit order flag that first is INET4
353        const INET4_INET6 = 0x10;
354        /// Sets the bit order flag that first is INET6
355        const INET6_INET4 = 0x20;
356        /// INET 4 is enabled
357        const INET4       = 0x01;
358        /// INET 6 is enabled
359        const INET6       = 0x02;
360    }
361}
362
363impl ResolveConfigFamily
364{
365    pub 
366    fn is_inet4(&self) -> bool
367    {
368        return self.contains(Self::INET4);
369    }
370
371    pub 
372    fn is_inet6(&self) -> bool
373    {
374        return self.contains(Self::INET6);
375    }
376
377    pub 
378    fn is_inet4_first(&self) -> bool
379    {
380        return self.contains(Self::INET4_INET6);
381    }
382
383    fn inverted_default() -> Self
384    {
385        return Self::INET6_INET4 | Self::INET4 | Self::INET6;
386    }
387}
388
389impl Default for ResolveConfigFamily
390{
391    fn default() -> Self 
392    {
393        return Self::INET4_INET6 | Self::INET4 | Self::INET6;
394    }
395}
396
397const FAMILY_INET4: &'static str = "inet4";
398const FAMILY_INET6: &'static str = "inet6";
399
400impl TryFrom<&[String]> for ResolveConfigFamily
401{
402    type Error = CDnsError;
403
404    fn try_from(value: &[String]) -> Result<Self, Self::Error> 
405    {
406        if value.is_empty() == true
407        {
408            internal_error!(CDnsErrorType::ConfigError, "'family' is empty");
409        }
410        else if value.len() > 2
411        {
412            internal_error!(CDnsErrorType::ConfigError, "'family' is set with too many paramenters: '{:?}'", value);
413        }
414
415        let mut family = 
416            if value[0] == FAMILY_INET4
417            {
418                // setting order 4 -> 6, enable 4
419                Self::INET4_INET6 | Self::INET4
420            }
421            else if value[0] == FAMILY_INET6
422            {
423                // setting order 6 -> 4, enable 6
424                Self::INET6_INET4 | Self::INET6
425            }
426            else
427            {
428                internal_error!(CDnsErrorType::ConfigError, "'family' is set with unknown parameter: '{}'", value[0]);
429            };
430
431        if value.len() == 2
432        {
433            if value[1] == FAMILY_INET4
434            {
435                if family.contains(Self::INET4) == true
436                {
437                    internal_error!(CDnsErrorType::ConfigError, "'family' is set with duplicate parameter: '{}'", value[1]);
438                }
439                
440                // enable INET4
441                family |= Self::INET4
442            }
443            else if value[1] == FAMILY_INET6
444            {
445                if family.contains(Self::INET6) == true
446                {
447                    internal_error!(CDnsErrorType::ConfigError, "'family' is set with duplicate parameter: '{}'", value[1]);
448                }
449                
450                // enable INET6
451                family |= Self::INET6
452            }
453            else
454            {
455                internal_error!(CDnsErrorType::ConfigError, "'family' is set with unknown parameter: '{}'", value[0]);
456            }
457        }
458
459        return Ok(family);
460    }
461}
462
463
464bitflags! {
465    /// An 'option' flags which can be set in /etc/resolv.conf
466    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
467    pub struct OptionFlags: u16 
468    {
469        /// Effective only if cdns-rs was built with debug support
470        const OPT_DEBUG                    = 0x0001;
471
472        /// Causes round-robin selection of name servers from among those listed (i)
473        const OPT_ROTATE                   = 0x0002;
474        
475        /// Disables the modern BIND checking of incoming hostnames and mail names 
476        /// for invalid characters such as underscore (_), non-ASCII, or control
477        /// characters
478        const OPT_NO_CHECK_NAMES           = 0x0004;
479
480        /// Trying an AAAA query before an A query
481        const OPT_INET6                    = 0x0008;
482
483        /// This option disables the parallel IPv4 and IPv6 lookups and
484        /// requests sequentially.
485        const OPT_SINGLE_REQUEST           = 0x0010;
486
487        /// Changes this behavior so that it will close the socket and open a new 
488        /// one before sending the second request
489        const OPT_SINGLE_REQUEST_REOPEN    = 0x0020;
490
491        ///This option disables automatic reloading of a changed configuration file. (i)
492        const OPT_NO_RELOAD                = 0x0040;
493
494        /// Controls the AD bit behavior of the stub resolver
495        const OPT_TRUST_AD                 = 0x0080;
496
497        /// This option forces the use of TCP for DNS resolutions.
498        const OPT_USE_VC                   = 0x0100;
499    }
500}
501
502impl Default for OptionFlags
503{
504    fn default() -> Self 
505    {
506        return Self::empty();
507    }
508}
509
510impl OptionFlags
511{
512    pub 
513    fn is_debug(&self) -> bool
514    {
515        return self.contains(Self::OPT_DEBUG);
516    }
517
518    pub 
519    fn is_rotate(&self) -> bool
520    {
521        return self.contains(Self::OPT_ROTATE);
522    }
523
524    pub 
525    fn is_no_check_names(&self) -> bool
526    {
527        return self.contains(Self::OPT_NO_CHECK_NAMES);
528    }
529
530    pub 
531    fn is_no_parallel(&self) -> bool
532    {
533        return self.contains(Self::OPT_SINGLE_REQUEST);
534    }
535
536    pub 
537    fn is_reopen_socket(&self) -> bool
538    {
539        return self.contains(Self::OPT_SINGLE_REQUEST_REOPEN);
540    }
541
542    pub 
543    fn is_force_tcp(&self) -> bool
544    {
545        return self.contains(Self::OPT_USE_VC);
546    }
547}
548
549
550#[derive(Clone, Debug, PartialEq, Eq)]
551pub struct ResolveConfig
552{
553    pub nameservers: Vec<Arc<ResolveConfEntry>>,
554    pub lookup: ResolveConfigLookup,
555    pub family: ResolveConfigFamily,
556    pub search_list: Vec<String>,
557    pub domain: Option<String>,
558    pub option_flags: OptionFlags,
559    pub ndots: usize,
560    pub timeout: u16,
561    pub attempts: usize,
562}
563
564impl Default for ResolveConfig
565{
566    fn default() -> Self 
567    {
568        return
569            ResolveConfig 
570            {
571                nameservers: vec![],
572                lookup: ResolveConfigLookup::default(),
573                family: ResolveConfigFamily::default(),
574                search_list: vec![],
575                domain: None,
576                option_flags: OptionFlags::default(),
577                ndots: 1,
578                timeout: 5,
579                attempts: 2,
580            };
581    }
582}
583
584impl ResolveConfig
585{
586    /// This function will either return the [RoundRobinIterator] which always 
587    /// starts from zero offset OR it will return the same instace but the initial
588    /// offset will be ROUND_ROBIN_CNT shared atomic counter. The ROUND_ROBIN_CNT
589    /// counts always forward. It is not expected that it will ever be overflowed.
590    /// 
591    /// # Returns
592    /// 
593    /// * [RoundRobinIterator] instance
594    pub 
595    fn get_resolvers_iter(&self) -> RoundRobinIterator<Arc<ResolveConfEntry>> 
596    {
597        if self.option_flags.is_rotate() == true
598        {
599            let start_index = ROUND_ROBIN_CNT.fetch_add(1, Ordering::SeqCst);
600
601            return RoundRobinIterator::new(start_index, self.nameservers.as_slice());
602        }
603        else
604        {
605            return RoundRobinIterator::new(0, self.nameservers.as_slice());
606        };
607    }
608
609    /// Splits the extension i.e nameserver 1.1.1.1%lo#@853#domainname where extension
610    /// starts right after the first # which is not separated by the space.
611    fn split_ext(field_data: &str) -> CDnsResult<(&str, u16, Option<String>)>
612    {
613        let splited = field_data.split("#").collect::<Vec<&str>>();
614        let mut port: u16 = 53;
615        let mut domain: Option<String> = None;
616        
617        if splited.is_empty() == true
618        {
619            return Ok((field_data, port, domain));
620        }
621
622        let mut splt_iter = splited.into_iter();
623        let without_ext = splt_iter.nth(0).unwrap();
624        
625
626        for ext in splt_iter
627        {
628            if ext.starts_with("@") == true
629            {
630                // port number
631                port = 
632                    u16::from_str_radix(&ext[1..], 10).map_err(|e|
633                        internal_error_map!(CDnsErrorType::InternalError, "can not parse port number, {}", e)
634                    )?;
635            }
636            else
637            {
638                // domain
639                domain = Some(ext.to_string());
640            }
641        }
642        
643        return Ok((without_ext, port, domain));
644    }
645
646    fn split_dns_ifr(field_data: &str, ifr: &IfInfo) -> CDnsResult<(IpAddr, SocketAddr)>
647    {
648        if let Some((ip, intf)) = field_data.split_once("%")
649        {   
650            let ip_addr: IpAddr = 
651                ip.parse().map_err(|e| 
652                    internal_error_map!(CDnsErrorType::InternalError, "parse IP: '{}' failed: '{}'", ip, e)
653                )?;
654
655
656            match unsafe { ifr.get_ifr_ip(intf, &ip_addr)? }
657            {
658                Some(ip_ifr) => 
659                    return Ok( (ip_addr, SocketAddr::from((ip_ifr, 0))) ),
660                None => 
661                    internal_error!(CDnsErrorType::InternalError, "interface: '{}' does not exist", intf),
662            }
663        }
664        else
665        {
666            let ip_addr: IpAddr = 
667                field_data.parse().map_err(|e|
668                    internal_error_map!(CDnsErrorType::InternalError, "parse IP: '{}' failed: '{}'", field_data, e)
669                )?;
670
671            let ip_ifr = bind_all(ip_addr);
672               /* match ip_addr
673                {
674                    IpAddr::V4(_) => 
675                        SocketAddr::from((IPV4_BIND_ALL, 0)),
676                    IpAddr::V6(_) => 
677                        SocketAddr::from((IPV6_BIND_ALL, 0)),
678                };*/
679
680            return Ok((ip_addr, ip_ifr))
681        }
682    }
683
684    #[inline]
685    fn set_option(
686        optname: &'static str, 
687        right: Option<&str>, 
688        option_flags: &mut OptionFlags, 
689        opt_flag: OptionFlags
690    ) -> CDnsResult<()>
691    {
692        if right.is_some() == true
693        {
694            internal_error!(CDnsErrorType::ConfigError, 
695                "in /etc/resolv.conf - 'option' '{}' is defined \
696                with parameter: '{}', but it does not have parameters", optname, right.unwrap());
697        }
698
699        option_flags.set(opt_flag, true);
700
701        return Ok(());
702    }
703
704    #[inline]
705    fn read_parameter<P: FromStr>(optname: &'static str, right: Option<&str>) -> CDnsResult<P>
706    {
707        if right.is_none() == true
708        {
709            internal_error!(CDnsErrorType::ConfigError, 
710                "in /etc/resolv.conf - 'option' '{}' is defined without parameter", optname);
711        }
712        else
713        {
714            return 
715                right.unwrap()
716                    .parse::<P>()
717                    .map_err(|_| 
718                        internal_error_map!(
719                            CDnsErrorType::ConfigError,
720                            "in /etc/resolv.conf - 'option' '{}' is defined with invalid \
721                            parameter: '{}'", optname, right.unwrap()
722                        )
723                    );
724        }
725    }
726
727    /// Parses the /etc/resolv.conf
728    /// Different OSes like GNU/Linux, FreeBSD and OpenBSD uses different 
729    /// configuration options. As this lib is licensed as EUPL, it is not really
730    /// the BSD way. The main implementation is like it is described in 
731    /// resolv.conf(5) of Linux man.
732    pub(crate)
733    fn parser_resolv_internal(file_content: &str) -> CDnsResult<Self>
734    {
735        let mut tk = ConfTokenizer::from_str(&file_content)?;
736
737        let mut dns_list: Vec<Arc<ResolveConfEntry>> = Vec::new();
738        let mut lookup_list: ResolveConfigLookup = ResolveConfigLookup::default();
739        let mut family_list: ResolveConfigFamily = ResolveConfigFamily::default();
740        
741        //Since glibc 2.26, the search list is unlimited.
742        let mut search_list: Vec<String> = Vec::new();
743
744        let mut domain: Option<String> = None;
745        let mut option_flags: OptionFlags = OptionFlags::empty();
746        let mut ndots: usize = 1;
747        let mut timeout: u16 = 5;
748        let mut attempts: usize = 2;
749
750        let if_info: IfInfo = unsafe { IfInfo::get_interfaces_info()? };
751
752        loop
753        {
754            let field_name = tk.read_next()?;
755
756            if field_name.is_none() == true
757            {
758                // reached EOF
759                break;
760            }
761
762            let field_name = field_name.unwrap();
763
764            if field_name == "nameserver"
765            {
766                // read second component
767                let Some(field_data) = tk.read_next()?
768                    else
769                    {
770                        // reached unexpected EOF
771                        internal_error!(CDnsErrorType::ConfigError, "unexpected EOF near nameserver");
772                    };
773
774                // parse IP and convert Interface to its IP i.e nameserver 127.0.0.1%lo#<Extension>
775                let ext_split_res = Self::split_ext(field_data);
776
777                let Ok((without_ext, port, domain)) = ext_split_res
778                    else
779                    {
780                        internal_error!(CDnsErrorType::ConfigError, "{}", ext_split_res.err().unwrap());
781                    };
782
783                let split = Self::split_dns_ifr(without_ext, &if_info);
784                
785                let Ok((dns_ip, ifr_ip)) = split
786                    else
787                    {
788                        internal_error!(CDnsErrorType::ConfigError, "{}", split.err().unwrap());
789                    };
790                    
791
792                dns_list.push(
793                    Arc::new(ResolveConfEntry::new(SocketAddr::new(dns_ip, port), domain, ifr_ip)?)
794                );
795                
796                
797            }
798            else if field_name == "lookup" // this is OpenBSD specific
799            {
800                // lookup file bind
801                let lookups = tk.read_upto_eol()?;
802
803                if lookups.len() == 0
804                {
805                    write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'lookup' is set but no args provided, setting defaults file, bind"));
806
807                    continue;
808                }
809
810                lookup_list = 
811                    match ResolveConfigLookup::try_from(lookups.as_slice())
812                    {
813                        Ok(r) => r,
814                        Err(e) => 
815                        {
816                            write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - {}", e));
817
818                            continue;
819                        }
820                    };
821            }
822            else if field_name == "family" // this is OpenBSD specific
823            {
824                // parse option
825                let families = tk.read_upto_eol()?;
826
827                if families.len() == 0
828                {
829                    write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'family' is set but no args provided, setting defaults inet4, inet6"));
830
831                    continue;
832                }
833
834                family_list = 
835                    match ResolveConfigFamily::try_from(families.as_slice())
836                    {
837                        Ok(r) => r,
838                        Err(e) =>
839                        {
840                            write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - {}", e));
841                            continue;
842                        }
843                    };
844            }
845            else if field_name == "search"
846            {
847                // parse search
848                let searches = tk.read_upto_eol()?;
849
850                if searches.len() == 0
851                {
852                    write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'search' is defined but empty"));
853                }
854                else
855                {
856                    search_list.extend(searches);
857                }
858            }
859            else if field_name == "domain"
860            {
861                let mut domains = tk.read_upto_eol()?;
862
863                if domain.is_some() == true
864                {
865                    write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'domain' is defined more than once: {:?} {:?}", domain, domains));
866                }
867
868                if domains.len() > 1
869                {
870                    write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'domain' is defined with more than 1 address: {:?}", domains));
871                }
872                else if domains.len() == 0
873                {
874                    write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'domain' is defined but empty"));
875                }
876                else
877                {
878                    domain = Some(domains.pop().unwrap());
879                }
880            }
881            else if field_name == "options"
882            {
883                
884                let options_vec = tk.read_upto_eol()?;
885
886                if options_vec.is_empty() == true
887                {
888                    write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'option' is empty!"));
889
890                    // reached EOL
891                    continue;
892                }
893
894                for option in options_vec
895                {
896                    let (left, right) = 
897                        match option.split_once(":")
898                        {
899                            Some((l, r)) => (l, Some(r)),
900                            None => (option.as_str(), None),
901                        };
902
903                    match left
904                    {
905                        // This option forces the use of TCP for DNS resolutions.
906                        "use-vc" | "usevc" | "tcp" => // linux | freebsd | openbsd
907                        {
908                            match Self::set_option("usevc", right, &mut option_flags, OptionFlags::OPT_USE_VC)
909                            {
910                                Err(e) => 
911                                    write_error!(e),
912                                Ok(_) => {}
913                            }
914                        },
915                        "debug" => 
916                        {
917                            match Self::set_option("debug", right, &mut option_flags, OptionFlags::OPT_DEBUG)
918                            {
919                                Err(e) => 
920                                    write_error!(e),
921                                Ok(_) => {}
922                            }
923                        },
924                        "ndots" => 
925                        {
926                            match Self::read_parameter("ndots", right)
927                            {
928                                Ok(val) => 
929                                {
930                                    ndots = if val > 15 { 15 } else { val };
931                                },
932                                Err(e) => 
933                                {
934                                    write_error!(e);
935                                }
936                            }
937                        },
938                        "timeout" => 
939                        {
940                            match Self::read_parameter::<u16>("timeout", right)
941                            {
942                                Ok(val) => 
943                                {
944                                    timeout = if val > 30 { 30 } else { val };
945                                },
946                                Err(e) => 
947                                {
948                                    write_error!(e);
949                                }
950                            }
951                        },
952                        "attempts" => 
953                        {
954                            match Self::read_parameter("attempts", right)
955                            {
956                                Ok(val) => 
957                                {
958                                    attempts = if val > 5 { 5 } else { val };
959                                },
960                                Err(e) => 
961                                {
962                                    write_error!(e);
963                                }
964                            }
965                        },
966                        "rotate" => 
967                        {
968                            match Self::set_option("rotate", right, &mut option_flags, OptionFlags::OPT_ROTATE)
969                            {
970                                Err(e) => 
971                                    write_error!(e),
972                                Ok(_) => {}
973                            }
974                        },
975                        "no-check-names" => 
976                        {
977                            match Self::set_option("no-check-names", right, &mut option_flags, OptionFlags::OPT_NO_CHECK_NAMES)
978                            {
979                                Err(e) => 
980                                    write_error!(e),
981                                Ok(_) => {}
982                            }
983                        },
984                        "no-reload" =>
985                        {
986                            match Self::set_option("no-reload", right, &mut option_flags, OptionFlags::OPT_NO_RELOAD)
987                            {
988                                Err(e) => 
989                                    write_error!(e),
990                                Ok(_) => {}
991                            }
992                        },
993                        "inet6" => 
994                        {
995                            // see 'family'
996                            match Self::set_option("inet6", right, &mut option_flags, OptionFlags::OPT_INET6)
997                            {
998                                Err(e) => 
999                                    write_error!(e),
1000                                Ok(_) => {}
1001                            }
1002                        },
1003                        "edns0" =>
1004                        {
1005                            // RFC 2671 not implemented! skip
1006                        },
1007                        "single-request" =>
1008                        {
1009                            match Self::set_option("single-request", right, &mut option_flags, OptionFlags::OPT_SINGLE_REQUEST)
1010                            {
1011                                Err(e) => 
1012                                    write_error!(e),
1013                                Ok(_) => {}
1014                            }
1015                        },
1016                        "single-request-reopen" => 
1017                        {
1018                            match Self::set_option("single-request-reopen", right, &mut option_flags, OptionFlags::OPT_SINGLE_REQUEST_REOPEN)
1019                            {
1020                                Err(e) => 
1021                                    write_error!(e),
1022                                Ok(_) => {}
1023                            }
1024                        },
1025                        "trust-ad" => 
1026                        {
1027                            match Self::set_option("trust-ad", right, &mut option_flags, OptionFlags::OPT_TRUST_AD)
1028                            {
1029                                Err(e) => 
1030                                    write_error!(e),
1031                                Ok(_) => {}
1032                            }
1033                        },
1034                        _ => 
1035                        {
1036                            write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'option' '{}' is unknown \
1037                                    with parameter: '{:?}'", left, right));
1038                        },
1039                    }
1040
1041                    
1042                }
1043            }
1044            else
1045            {
1046                // skip until end of line or EOF
1047                tk.skip_upto_eol();
1048            }
1049        }        
1050
1051        if option_flags.contains(OptionFlags::OPT_INET6) == true
1052        {
1053            family_list = ResolveConfigFamily::inverted_default();
1054        }
1055    
1056
1057        return Ok(
1058            Self
1059            {
1060                nameservers: dns_list,
1061                lookup: lookup_list,
1062                family: family_list,
1063                search_list: search_list,
1064                domain: domain,
1065                option_flags: option_flags,
1066                ndots: ndots,
1067                timeout: timeout,
1068                attempts: attempts,
1069            }
1070        );
1071    }
1072}
1073
1074#[cfg(feature = "use_sync")]
1075#[cfg(test)]
1076mod tests
1077{
1078    use std::{net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, str::FromStr};
1079
1080    use crate::{cfg_resolv_parser::{OptionFlags, ResolveConfigFamily, ResolveConfigLookup}, sync::ResolveConfig};
1081
1082    #[test]
1083    fn test_parser_resolv_tls_0() 
1084    {
1085        let ip1 = SocketAddr::new("1.1.1.1".parse::<IpAddr>().unwrap(), 853);
1086        let tls_domain1 = Some(String::from("cloudflare-dns.com"));
1087        let ip2 = SocketAddr::new("2606:4700:4700::1001".parse::<IpAddr>().unwrap(), 853);
1088        let tls_domain2 = Some(String::from("cloudflare-dns.com"));
1089        let ip3 = SocketAddr::new("127.0.0.1".parse().unwrap(), 853);
1090        let tls_domain3 = Some(String::from("localdomain"));
1091        let ip3_if: SocketAddr = SocketAddr::from((Ipv4Addr::from_str("127.0.0.1").unwrap(), 0));
1092        let ip4 = SocketAddr::new("8.8.8.8".parse().unwrap(), 55);
1093        let ip5 = SocketAddr::new("1.0.0.2".parse().unwrap(), 53);
1094        let ip6 = SocketAddr::new("1.1.1.1".parse::<IpAddr>().unwrap(), 443);
1095
1096        let test = 
1097        "nameserver 1.1.1.1#@853#cloudflare-dns.com\n\
1098        nameserver 2606:4700:4700::1001#@853#cloudflare-dns.com\n\
1099        nameserver 127.0.0.1%lo#@853#localdomain\n\
1100        nameserver 8.8.8.8#@55\n\
1101        nameserver 1.0.0.2 #@853\n\
1102        nameserver 1.1.1.1#@443#https://cloudflare-dns.com/dns-query\n";
1103
1104        let res = ResolveConfig::parser_resolv_internal(test);
1105
1106        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1107        let res = res.unwrap();
1108
1109        assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET6 | ResolveConfigFamily::INET4);
1110
1111        assert_eq!(res.nameservers[0].ip, ip1);
1112        assert_eq!(res.nameservers[0].tls_domain.as_ref(), tls_domain1.as_ref());
1113        assert_eq!(res.nameservers[0].https_req_path, None);
1114        assert_eq!(res.nameservers[1].ip, ip2);
1115        assert_eq!(res.nameservers[1].tls_domain.as_ref(), tls_domain2.as_ref());
1116        assert_eq!(res.nameservers[2].ip, ip3);
1117        assert_eq!(res.nameservers[2].tls_domain.as_ref(), tls_domain3.as_ref());
1118        assert_eq!(res.nameservers[2].adapter_ip, ip3_if);
1119        assert_eq!(res.nameservers[3].ip, ip4);
1120        assert_eq!(res.nameservers[4].ip, ip5);
1121        assert_eq!(res.nameservers[5].ip, ip6);
1122        assert_eq!(res.nameservers[5].tls_domain, Some("cloudflare-dns.com".to_string()));
1123        assert_eq!(res.nameservers[5].https_req_path, Some("dns-query".to_string()));
1124    }
1125
1126    #[test]
1127    fn test_parser_resolv_internal_0() 
1128    {
1129        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
1130        let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
1131        let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
1132        let ip4 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
1133        let ip4_if: SocketAddr = SocketAddr::from((Ipv4Addr::from_str("127.0.0.1").unwrap(), 0));
1134        let ip5 = SocketAddr::new("fe80::1".parse().unwrap(), 53);
1135        let ip5_if: SocketAddr = SocketAddr::from((Ipv6Addr::from_str("::1").unwrap(), 0));
1136
1137        let test = 
1138        "nameserver 192.168.2.1\n\
1139        nameserver  127.0.0.1\n\
1140        lookup bind\n\
1141        nameserver  8.8.8.8\n
1142        nameserver 127.0.0.1%lo\n
1143        nameserver fe80::1%lo\n";
1144
1145
1146        let res = ResolveConfig::parser_resolv_internal(test);
1147    
1148        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1149        let res = res.unwrap();
1150
1151        assert_eq!(res.lookup, ResolveConfigLookup::BIND);
1152
1153        assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET6 | ResolveConfigFamily::INET4);
1154
1155        assert_eq!(res.nameservers[0].ip, ip1);
1156        assert_eq!(res.nameservers[1].ip, ip2);
1157        assert_eq!(res.nameservers[2].ip, ip3);
1158        assert_eq!(res.nameservers[3].ip, ip4);
1159        assert_eq!(res.nameservers[3].adapter_ip, ip4_if);
1160        assert_eq!(res.nameservers[4].ip, ip5);
1161        assert_eq!(res.nameservers[4].adapter_ip, ip5_if);
1162    }
1163
1164    #[test]
1165    fn test_parser_resolv_internal() 
1166    {
1167        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
1168        let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
1169        let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
1170
1171        let test = 
1172        "nameserver 192.168.2.1\n\
1173        nameserver  127.0.0.1\n\
1174        lookup bind file\n\
1175        nameserver  8.8.8.8";
1176
1177        let res = ResolveConfig::parser_resolv_internal(test);
1178        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1179        let res = res.unwrap();
1180
1181        assert_eq!(res.lookup, ResolveConfigLookup::BIND | ResolveConfigLookup::FILE | ResolveConfigLookup::BIND_FILE);
1182
1183        assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET6 | ResolveConfigFamily::INET4);
1184
1185        assert_eq!(res.nameservers[0].ip, ip1);
1186        assert_eq!(res.nameservers[1].ip, ip2);
1187        assert_eq!(res.nameservers[2].ip, ip3);
1188    }
1189
1190    #[test]
1191    fn test_parser_resolv_internal2() 
1192    {
1193        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
1194        let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
1195        let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
1196
1197        let test = 
1198        "# test 12345\n\
1199        # nameserver 192.168.3.1
1200        nameserver 192.168.2.1  \n\
1201        nameserver  127.0.0.1   \n\
1202        lookup file\n\
1203        family inet4\n\
1204        nameserver  8.8.8.8\n";
1205
1206        let res = ResolveConfig::parser_resolv_internal(test);
1207        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1208        let res = res.unwrap();
1209
1210        assert_eq!(res.lookup, ResolveConfigLookup::FILE);
1211
1212        assert_eq!(res.family, ResolveConfigFamily::INET4 | ResolveConfigFamily::INET4_INET6);
1213
1214        assert_eq!(res.nameservers.len(), 3);
1215        assert_eq!(res.nameservers[0].ip, ip1);
1216        assert_eq!(res.nameservers[1].ip, ip2);
1217        assert_eq!(res.nameservers[2].ip, ip3);
1218    }
1219
1220    #[test]
1221    fn test_parser_resolv_internal3() 
1222    {
1223        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
1224        let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
1225        let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
1226
1227        let test = 
1228        "# test 12345\n\
1229        # nameserver 192.168.3.1
1230        nameserver 192.168.2.1  \n\
1231        nameserver  127.0.0.1   \n\
1232        lookup file\n\
1233        family inet4\n\
1234        nameserver  8.8.8.8\n
1235        options attempts:1 timeout:3 debug use-vc single-request-reopen\n
1236        search localdomain";
1237
1238        let res = ResolveConfig::parser_resolv_internal(test);
1239        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1240        let res = res.unwrap();
1241
1242        assert_eq!(res.lookup, ResolveConfigLookup::FILE);
1243
1244        assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET4);
1245
1246        assert_eq!(res.nameservers.len(), 3);
1247        assert_eq!(res.nameservers[0].ip, ip1);
1248        assert_eq!(res.nameservers[1].ip, ip2);
1249        assert_eq!(res.nameservers[2].ip, ip3);
1250
1251        assert_eq!(res.search_list[0], "localdomain");
1252
1253        assert_eq!(res.timeout, 3);
1254        assert_eq!(res.attempts, 1);
1255
1256        assert_eq!(res.option_flags.contains(OptionFlags::OPT_DEBUG), true);
1257        assert_eq!(res.option_flags.contains(OptionFlags::OPT_USE_VC), true);
1258        assert_eq!(res.option_flags.contains(OptionFlags::OPT_SINGLE_REQUEST_REOPEN), true);
1259    }
1260
1261    #[test]
1262    fn test_parser_resolv_internal4() 
1263    {
1264        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
1265        let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
1266        let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
1267
1268        let test = 
1269    "\
1270    # test 12345\n\
1271    # nameserver    192.168.3.1\n\
1272    nameserver  192.168.2.1  \n\
1273    nameserver       127.0.0.1   \n\
1274    lookup file\n\
1275    family inet4       \n\
1276    nameserver  8.8.8.8\n\
1277    options    attempts:1    timeout:3 debug use-vc     single-request-reopen\n\
1278    search   localdomain\n\
1279    ";
1280
1281
1282        let res = ResolveConfig::parser_resolv_internal(test);
1283        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1284        let res = res.unwrap();
1285
1286        assert_eq!(res.lookup, ResolveConfigLookup::FILE);
1287
1288        assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET4);
1289
1290        assert_eq!(res.nameservers.len(), 3);
1291        assert_eq!(res.nameservers[0].ip, ip1);
1292        assert_eq!(res.nameservers[1].ip, ip2);
1293        assert_eq!(res.nameservers[2].ip, ip3);
1294
1295        assert_eq!(res.search_list[0], "localdomain");
1296
1297        assert_eq!(res.timeout, 3);
1298        assert_eq!(res.attempts, 1);
1299
1300        assert_eq!(res.option_flags.contains(OptionFlags::OPT_DEBUG), true);
1301        assert_eq!(res.option_flags.contains(OptionFlags::OPT_USE_VC), true);
1302        assert_eq!(res.option_flags.contains(OptionFlags::OPT_SINGLE_REQUEST_REOPEN), true);
1303    }
1304
1305    #[test]
1306    fn test_roundrobin_iter_1() 
1307    {
1308        use std::time::Instant;
1309
1310        fn check_order(res: &ResolveConfig, ipl: &[SocketAddr], indx: usize)
1311        {
1312            let mut i = indx;
1313            let assert_i = (indx + ipl.len()) % ipl.len();
1314            let now = Instant::now();
1315
1316            let itr = res.get_resolvers_iter();
1317
1318            assert_eq!(itr.len(), ipl.len());
1319
1320            for n in itr
1321            {
1322                //println!("cmp: {} {}", n.ip, ipl[i]);
1323                assert_eq!(n.ip, ipl[i]);
1324
1325                i += 1;
1326
1327                i %= ipl.len();
1328
1329            }
1330
1331            assert_eq!(i, assert_i);
1332
1333            let elapsed = now.elapsed();
1334            println!("Iter Elapsed: {:.2?}", elapsed);
1335        }
1336
1337        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(),  53);
1338
1339        let ip_list: Vec<SocketAddr> = vec![ip1];
1340
1341        let test = 
1342    "\
1343    # test 12345\n\
1344    # nameserver    192.168.3.1\n\
1345    nameserver  192.168.2.1  \n\
1346    #nameserver       127.0.0.1   \n\
1347    lookup file\n\
1348    family inet4       \n\
1349    #nameserver  8.8.8.8\n\
1350    options    attempts:1  rotate   timeout:3 debug use-vc     single-request-reopen\n\
1351    search   localdomain\n\
1352    ";
1353    
1354    let now = Instant::now();
1355
1356
1357        let res = ResolveConfig::parser_resolv_internal(test);
1358
1359    let elapsed = now.elapsed();
1360    println!("parser_resolv_internal elapsed: {:.2?}", elapsed);
1361
1362        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1363        let res = res.unwrap();
1364
1365        check_order(&res, &ip_list, 0);
1366        check_order(&res, &ip_list, 0);
1367        check_order(&res, &ip_list, 0);
1368        check_order(&res, &ip_list, 0);
1369        check_order(&res, &ip_list, 0);
1370        check_order(&res, &ip_list, 0);
1371        check_order(&res, &ip_list, 0);
1372        check_order(&res, &ip_list, 0);
1373        check_order(&res, &ip_list, 0);
1374    }
1375
1376    #[test]
1377    fn test_roundrobin_iter() 
1378    {
1379        use std::time::Instant;
1380
1381        fn check_order(res: &ResolveConfig, ipl: &[SocketAddr], indx: usize)
1382        {
1383            let mut i = indx;
1384            let assert_i = (indx + ipl.len()) % ipl.len();
1385            let now = Instant::now();
1386
1387            let itr = res.get_resolvers_iter();
1388
1389            assert_eq!(itr.len(), ipl.len());
1390
1391            for n in itr
1392            {
1393                //println!("cmp: {} {}", n.ip, ipl[i]);
1394                assert_eq!(n.ip, ipl[i]);
1395
1396                i += 1;
1397
1398                i %= ipl.len();
1399
1400            }
1401
1402            assert_eq!(i, assert_i);
1403
1404            let elapsed = now.elapsed();
1405            println!("Iter Elapsed: {:.2?}", elapsed);
1406        }
1407
1408        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
1409        let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
1410        let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
1411
1412        let ip_list: Vec<SocketAddr> = vec![ip1, ip2, ip3];
1413
1414        let test = 
1415    "\
1416    # test 12345\n\
1417    # nameserver    192.168.3.1\n\
1418    nameserver  192.168.2.1  \n\
1419    nameserver       127.0.0.1   \n\
1420    lookup file\n\
1421    family inet4       \n\
1422    nameserver  8.8.8.8\n\
1423    options    attempts:1  rotate   timeout:3 debug use-vc     single-request-reopen\n\
1424    search   localdomain\n\
1425    ";
1426    
1427    let now = Instant::now();
1428
1429        let res = ResolveConfig::parser_resolv_internal(test);
1430
1431    let elapsed = now.elapsed();
1432    println!("parser_resolv_internal elapsed: {:.2?}", elapsed);
1433
1434        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1435        let res = res.unwrap();
1436
1437        check_order(&res, &ip_list, 0);
1438        check_order(&res, &ip_list, 1);
1439        check_order(&res, &ip_list, 2);
1440        check_order(&res, &ip_list, 0);
1441        check_order(&res, &ip_list, 1);
1442        check_order(&res, &ip_list, 2);
1443        check_order(&res, &ip_list, 0);
1444        check_order(&res, &ip_list, 1);
1445        check_order(&res, &ip_list, 2);
1446    }
1447
1448    #[test]
1449    fn test_direct_iter() 
1450    {
1451        use std::time::Instant;
1452
1453        let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
1454        let ip2 = SocketAddr::new("192.168.2.2".parse().unwrap(), 53);
1455
1456        let test = 
1457    "\
1458    # test 12345\n\
1459    # nameserver    192.168.3.1\n\
1460    nameserver  192.168.2.1  \n\
1461    nameserver  192.168.2.2  \n\
1462    lookup file\n\
1463    family inet4       \n\
1464    options    attempts:1  timeout:3 debug use-vc     single-request-reopen\n\
1465    search   localdomain\n\
1466    ";
1467    
1468    let now = Instant::now();
1469
1470
1471        let res = ResolveConfig::parser_resolv_internal(test);
1472
1473    let elapsed = now.elapsed();
1474    println!("Elapsed: {:.2?}", elapsed);
1475
1476        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1477        let res = res.unwrap();
1478
1479        let mut i: usize = 0;
1480        
1481        let now = Instant::now();
1482
1483        for n in res.get_resolvers_iter()
1484        {
1485            match i
1486            {
1487                0 => { assert_eq!(n.ip, ip1); i += 1; },
1488                1 => { assert_eq!(n.ip, ip2); i += 1; },
1489                _ => panic!("!")
1490            }
1491        }
1492
1493        let elapsed = now.elapsed();
1494        println!("Iter Elapsed: {:.2?}", elapsed);
1495
1496        assert_eq!(i, 2);
1497
1498        i = 0;
1499        for n in res.get_resolvers_iter()
1500        {
1501            match i
1502            {
1503                0 => { assert_eq!(n.ip, ip1); i += 1; },
1504                1 => { assert_eq!(n.ip, ip2); i += 1; },
1505                _ => panic!("!")
1506            }
1507        }
1508
1509        assert_eq!(i, 2);
1510    }
1511
1512}