chrootable-https 0.16.0

Sandbox friendly https client
Documentation
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(),
        ]);
    }
}