1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/// functionality to deal with the handling of /etc/hosts formatted files
use std::{collections::HashMap, net::IpAddr, path::PathBuf, str::FromStr};
use tracing::warn;
use trust_dns_server::client::rr::Name;
use crate::traits::ToHostname;
pub type HostsFile = HashMap<IpAddr, Vec<Name>>;
const WHITESPACE_SPLIT: &str = r"\s+";
const COMMENT_MATCH: &str = r"^\s*#";
/// Parses an /etc/hosts-formatted file into a mapping of ip -> [name]. Used to populate the
/// authority.
pub fn parse_hosts(
hosts_file: Option<PathBuf>,
domain_name: Name,
) -> Result<HostsFile, std::io::Error> {
let mut input: HostsFile = HashMap::new();
if let None = hosts_file {
return Ok(input);
}
let whitespace = regex::Regex::new(WHITESPACE_SPLIT).unwrap();
let comment = regex::Regex::new(COMMENT_MATCH).unwrap();
let content = std::fs::read_to_string(hosts_file.clone().unwrap())?;
for line in content.lines() {
if line.trim().len() == 0 {
continue;
}
// after whitespace is ruled out as the only thing on the line, the line is split by ..
// whitespace and the parts iterated.
let mut ary = whitespace.split(line);
// the first item will be the ip
match ary.next() {
Some(ip) => {
// technically we're still matching the head of the line at this point. if it's a
// comment, bail.
if comment.is_match(ip) {
continue;
}
// ensure we have an IP, again, this is still the first field.
match IpAddr::from_str(ip) {
Ok(parsed_ip) => {
// now that we have the ip, it's all names now.
let mut v: Vec<Name> = Vec::new();
// continue to iterate over the hosts. If we encounter a comment, stop
// processing.
for host in ary.take_while(|h| !comment.is_match(h)) {
let fqdn = match host.to_fqdn(domain_name.clone()) {
Ok(fqdn) => Some(fqdn),
Err(e) => {
warn!("Invalid host {}: {:?}", host, e);
None
}
};
if let Some(fqdn) = fqdn {
v.push(fqdn)
}
}
// if we have a valid ip in the collection already, append, don't clobber
// it.
if input.contains_key(&parsed_ip) {
input.get_mut(&parsed_ip).unwrap().append(&mut v);
} else {
input.insert(parsed_ip, v);
}
}
Err(e) => {
warn!("Couldn't parse {}: {}", ip, e);
}
}
}
None => {}
}
}
Ok(input)
}