chrootable_https/dns/system_conf/
unix.rs

1use crate::errors::*;
2use std::fs;
3use std::net::SocketAddr;
4
5pub fn read_system_conf() -> Result<Vec<SocketAddr>> {
6    let r = fs::read_to_string("/etc/resolv.conf")?;
7    parse_conf(&r)
8}
9
10fn parse_conf(data: &str) -> Result<Vec<SocketAddr>> {
11    let mut ns = Vec::new();
12
13    for line in data.lines() {
14        let mut words = line
15            .split(|c| c == ';' || c == '#')
16            .next()
17            .ok_or_else(|| format_err!("Invalid resolv.conf line"))?
18            .split_whitespace();
19
20        if words.next() == Some("nameserver") {
21            if let Some(ip) = words.next() {
22                if let Ok(ip) = ip.parse() {
23                    let srv = SocketAddr::new(ip, 53);
24                    ns.push(srv);
25
26                    if words.next().is_some() {
27                        warn!("trailing resolv.conf data in line {:?}", line);
28                    }
29                }
30            }
31        }
32    }
33
34    Ok(ns)
35}
36
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn parse_systemd_resolved_stub() {
44        let data = r#"# This file belongs to man:systemd-resolved(8). Do not edit.
45#
46# This is a static resolv.conf file for connecting local clients to the
47# internal DNS stub resolver of systemd-resolved. This file lists no search
48# domains.
49#
50# Run "resolvectl status" to see details about the uplink DNS servers
51# currently in use.
52#
53# Third party programs must not access this file directly, but only through the
54# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
55# replace this symlink by a static file or a different symlink.
56#
57# See man:systemd-resolved.service(8) for details about the supported modes of
58# operation for /etc/resolv.conf.
59
60nameserver 127.0.0.53
61options edns0 trust-ad
62"#;
63        let r = parse_conf(data).unwrap();
64        assert_eq!(r, vec![
65            "127.0.0.53:53".parse::<SocketAddr>().unwrap(),
66        ]);
67    }
68
69    #[test]
70    fn parse_resolvconf() {
71        let data = r#"# Generated by resolvconf
72search lan
73nameserver 192.168.1.1
74nameserver fe80::eeee:ff:feed:f00d%enp0s25
75"#;
76        let r = parse_conf(data).unwrap();
77        assert_eq!(r, vec![
78            "192.168.1.1:53".parse::<SocketAddr>().unwrap(),
79        ]);
80    }
81
82    #[test]
83    fn parse_cloudflare() {
84        let data = r#"nameserver 1.1.1.1
85nameserver 1.0.0.1
86nameserver 2606:4700:4700::1111
87nameserver 2606:4700:4700::1001
88"#;
89        let r = parse_conf(data).unwrap();
90        assert_eq!(r, vec![
91            "1.1.1.1:53".parse::<SocketAddr>().unwrap(),
92            "1.0.0.1:53".parse::<SocketAddr>().unwrap(),
93            "[2606:4700:4700::1111]:53".parse::<SocketAddr>().unwrap(),
94            "[2606:4700:4700::1001]:53".parse::<SocketAddr>().unwrap(),
95        ]);
96    }
97
98    #[test]
99    fn ignore_garbage() {
100        let data = r#"asdf
101  foo
102  #  x
103nameserver asdf
104bar
105"#;
106        let r = parse_conf(data).unwrap();
107        assert_eq!(r, vec![]);
108    }
109
110    #[test]
111    fn ignore_commented_nameserver() {
112        let data = r#"#nameserver 127.0.0.1"#;
113        let r = parse_conf(data).unwrap();
114        assert_eq!(r, vec![]);
115    }
116
117    #[test]
118    fn ignore_trailing_spaces() {
119        let data = " \t  nameserver 127.0.0.1";
120        let r = parse_conf(data).unwrap();
121        assert_eq!(r, vec![
122            "127.0.0.1:53".parse::<SocketAddr>().unwrap(),
123        ]);
124    }
125}