use crate::errors::*;
use std::fs;
use std::net::SocketAddr;
pub fn read_system_conf() -> Result<Vec<SocketAddr>> {
let r = fs::read_to_string("/etc/resolv.conf")?;
parse_conf(&r)
}
fn parse_conf(data: &str) -> Result<Vec<SocketAddr>> {
let mut ns = Vec::new();
for line in data.lines() {
let mut words = line
.split(|c| c == ';' || c == '#')
.next()
.ok_or_else(|| format_err!("Invalid resolv.conf line"))?
.split_whitespace();
if words.next() == Some("nameserver") {
if let Some(ip) = words.next() {
if let Ok(ip) = ip.parse() {
let srv = SocketAddr::new(ip, 53);
ns.push(srv);
if words.next().is_some() {
warn!("trailing resolv.conf data in line {:?}", line);
}
}
}
}
}
Ok(ns)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_systemd_resolved_stub() {
let data = r#"# This file belongs to man:systemd-resolved(8). Do not edit.
#
# This is a static resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists no search
# domains.
#
# Run "resolvectl status" to see details about the uplink DNS servers
# currently in use.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.
nameserver 127.0.0.53
options edns0 trust-ad
"#;
let r = parse_conf(data).unwrap();
assert_eq!(r, vec![
"127.0.0.53:53".parse::<SocketAddr>().unwrap(),
]);
}
#[test]
fn parse_resolvconf() {
let data = r#"# Generated by resolvconf
search lan
nameserver 192.168.1.1
nameserver fe80::eeee:ff:feed:f00d%enp0s25
"#;
let r = parse_conf(data).unwrap();
assert_eq!(r, vec![
"192.168.1.1:53".parse::<SocketAddr>().unwrap(),
]);
}
#[test]
fn parse_cloudflare() {
let data = r#"nameserver 1.1.1.1
nameserver 1.0.0.1
nameserver 2606:4700:4700::1111
nameserver 2606:4700:4700::1001
"#;
let r = parse_conf(data).unwrap();
assert_eq!(r, vec![
"1.1.1.1:53".parse::<SocketAddr>().unwrap(),
"1.0.0.1:53".parse::<SocketAddr>().unwrap(),
"[2606:4700:4700::1111]:53".parse::<SocketAddr>().unwrap(),
"[2606:4700:4700::1001]:53".parse::<SocketAddr>().unwrap(),
]);
}
#[test]
fn ignore_garbage() {
let data = r#"asdf
foo
# x
nameserver asdf
bar
"#;
let r = parse_conf(data).unwrap();
assert_eq!(r, vec![]);
}
#[test]
fn ignore_commented_nameserver() {
let data = r#"#nameserver 127.0.0.1"#;
let r = parse_conf(data).unwrap();
assert_eq!(r, vec![]);
}
#[test]
fn ignore_trailing_spaces() {
let data = " \t nameserver 127.0.0.1";
let r = parse_conf(data).unwrap();
assert_eq!(r, vec![
"127.0.0.1:53".parse::<SocketAddr>().unwrap(),
]);
}
}