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