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