chrootable_https/dns/system_conf/
unix.rs1use 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}