1use std::io;
2use std::path::{Path, PathBuf};
3use tokio::fs::{read_dir, read_to_string};
4
5use dns_types::hosts::types::Hosts;
6use dns_types::zones::types::{Zone, Zones};
7
8pub async fn load_zone_configuration(
11 hosts_files: &[PathBuf],
12 hosts_dirs: &[PathBuf],
13 zone_files: &[PathBuf],
14 zone_dirs: &[PathBuf],
15) -> Option<Zones> {
16 let mut is_error = false;
17 let mut hosts_file_paths = Vec::from(hosts_files);
18 let mut zone_file_paths = Vec::from(zone_files);
19
20 for path in zone_dirs {
21 match get_files_from_dir(path).await {
22 Ok(mut paths) => zone_file_paths.append(&mut paths),
23 Err(error) => {
24 tracing::warn!(?path, ?error, "could not read zone directory");
25 is_error = true;
26 }
27 }
28 }
29 for path in hosts_dirs {
30 match get_files_from_dir(path).await {
31 Ok(mut paths) => hosts_file_paths.append(&mut paths),
32 Err(error) => {
33 tracing::warn!(?path, ?error, "could not read hosts directory");
34 is_error = true;
35 }
36 }
37 }
38
39 let mut combined_zones = Zones::new();
40 for path in &zone_file_paths {
41 match zone_from_file(Path::new(path)).await {
42 Ok(Ok(zone)) => combined_zones.insert_merge(zone),
43 Ok(Err(error)) => {
44 tracing::warn!(?path, ?error, "could not parse zone file");
45 is_error = true;
46 }
47 Err(error) => {
48 tracing::warn!(?path, ?error, "could not read zone file");
49 is_error = true;
50 }
51 }
52 }
53
54 let mut combined_hosts = Hosts::default();
55 for path in &hosts_file_paths {
56 match hosts_from_file(Path::new(path)).await {
57 Ok(Ok(hosts)) => combined_hosts.merge(hosts),
58 Ok(Err(error)) => {
59 tracing::warn!(?path, ?error, "could not parse hosts file");
60 is_error = true;
61 }
62 Err(error) => {
63 tracing::warn!(?path, ?error, "could not read hosts file");
64 is_error = true;
65 }
66 }
67 }
68
69 if is_error {
70 None
71 } else {
72 combined_zones.insert_merge(combined_hosts.into());
73 Some(combined_zones)
74 }
75}
76
77async fn hosts_from_file<P: AsRef<Path>>(
79 path: P,
80) -> io::Result<Result<Hosts, dns_types::hosts::deserialise::Error>> {
81 let data = read_to_string(path).await?;
82 Ok(Hosts::deserialise(&data))
83}
84
85async fn zone_from_file<P: AsRef<Path>>(
94 path: P,
95) -> io::Result<Result<Zone, dns_types::zones::deserialise::Error>> {
96 let data = read_to_string(path).await?;
97 Ok(Zone::deserialise(&data))
98}
99
100async fn get_files_from_dir(dir: &Path) -> io::Result<Vec<PathBuf>> {
102 let mut out = Vec::new();
103
104 let mut reader = read_dir(dir).await?;
105 while let Some(entry) = reader.next_entry().await? {
106 let path = entry.path();
107 if !path.is_dir() {
108 out.push(path);
109 }
110 }
111
112 out.sort();
113 Ok(out)
114}