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