cdns_rs/
cfg_host_parser.rs1use std::borrow::Borrow;
27use std::collections::HashSet;
28use std::hash::{Hash, Hasher};
29use std::net::IpAddr;
30
31use crate::a_sync::QType;
32use crate::a_sync::common::HOST_CFG_PATH;
33use crate::{error::*, writer_error};
34use crate::tokenizer::*;
35
36
37#[derive(Clone, Debug)]
41pub struct HostnameEntry
42{
43 ip: IpAddr,
44 hostnames: Vec<String>,
45}
46
47impl Eq for HostnameEntry {}
48
49impl PartialEq for HostnameEntry
50{
51 fn eq(&self, other: &HostnameEntry) -> bool
52 {
53 return self.ip == other.ip;
54 }
55}
56
57impl Borrow<IpAddr> for HostnameEntry
58{
59 fn borrow(&self) -> &IpAddr
60 {
61 return &self.ip;
62 }
63}
64
65impl Hash for HostnameEntry
66{
67 fn hash<H: Hasher>(&self, state: &mut H)
68 {
69 self.ip.hash(state);
70 }
71}
72
73
74impl HostnameEntry
75{
76 pub
78 fn get_ip(&self) -> &IpAddr
79 {
80 return &self.ip;
81 }
82
83 pub
85 fn get_hostnames(&self) -> &[String]
86 {
87 return self.hostnames.as_slice();
88 }
89
90 pub
92 fn get_hostnames_iter(&self) -> std::slice::Iter<'_, String>
93 {
94 return self.hostnames.iter();
95 }
96}
97
98#[derive(Clone, Debug)]
99pub struct HostConfig
100{
101 hostnames: HashSet<HostnameEntry>
102}
103
104impl Default for HostConfig
105{
106 fn default() -> Self
107 {
108 return
109 Self
110 {
111 hostnames: Default::default(),
112 };
113 }
114}
115
116impl HostConfig
117{
118 pub
119 fn is_empty(&self) -> bool
120 {
121 return self.hostnames.is_empty();
122 }
123
124 pub
125 fn search_by_ip(&self, ip: &IpAddr) -> Option<&HostnameEntry>
126 {
127 return self.hostnames.get(ip);
128 }
129
130 pub
132 fn search_by_fqdn(&self, qtype: &QType, name: &str) -> Option<&HostnameEntry>
133 {
134 for host in self.hostnames.iter()
135 {
136 for fqdn in host.hostnames.iter()
137 {
138 if name == fqdn.as_str() && qtype.ipaddr_match(&host.ip)
140 {
141 return Some(host);
142 }
143 }
144 }
145
146 return None;
147 }
148
149 pub
150 fn parse_host_file_internal(file_content: String, f: &mut Writer) -> CDnsResult<Self>
151 {
152 let mut tk = ConfTokenizer::from_str(&file_content)?;
153 let mut he_list: HashSet<HostnameEntry> = HashSet::new();
154
155 loop
156 {
157 let field_ip = tk.read_next()?;
158
159 if field_ip.is_none() == true
160 {
161 break;
163 }
164
165 let ip: IpAddr =
168 match field_ip.unwrap().parse()
169 {
170 Ok(r) => r,
171 Err(_e) =>
172 {
173 tk.skip_upto_eol();
175 continue;
176 }
177 };
178
179 let hostnames = tk.read_upto_eol()?;
180
181 if hostnames.len() > 0
182 {
183 let he = HostnameEntry{ ip: ip, hostnames: hostnames };
184 he_list.insert(he);
185 }
186 else
187 {
188 writer_error!(f, "in file: '{}' IP is not defined with domain name: '{}'\n", HOST_CFG_PATH, ip);
189 }
190 }
191
192 if he_list.len() == 0
193 {
194 writer_error!(f, "file: '{}' file is empty or damaged!\n", HOST_CFG_PATH);
195 }
196
197 return Ok(
198 Self
199 {
200 hostnames: he_list
201 }
202 );
203 }
204}
205
206
207#[test]
208fn test_parse_host_file_0()
209{
210 let hosts1: Vec<&'static str> = vec!["debian-laptop"];
211 let hosts2: Vec<&'static str> = vec!["localhost", "ip6-localhost", "ip6-loopback"];
212 let hosts3: Vec<&'static str> = vec!["ip6-allnodes"];
213 let hosts4: Vec<&'static str> = vec!["ip6-allrouters"];
214
215 let ip1: IpAddr = "127.0.1.1".parse().unwrap();
216 let ip2: IpAddr = "::1".parse().unwrap();
217 let ip3: IpAddr = "ff02::1".parse().unwrap();
218 let ip4: IpAddr = "ff02::2".parse().unwrap();
219
220 let ip_list =
221 vec![
222 (ip1, hosts1),
223 (ip2, hosts2),
224 (ip3, hosts3),
225 (ip4, hosts4)
226 ];
227
228 let test =
229 "127.0. 0.1 localhost
230 127.0.1.1 debian-laptop
231
232 # The following lines are desirable for IPv6 capable hosts
233 ::1 localhost ip6-localhost ip6-loopback
234 ff02::1 ip6-allnodes
235 ff02::2 ip6-allrouters".to_string();
236
237 let mut writer = Writer::new();
238
239 let p = HostConfig::parse_host_file_internal(test, &mut writer);
240 assert_eq!(p.is_ok(), true, "{}", p.err().unwrap());
241
242 let p = p.unwrap();
243
244 for (ip, host) in ip_list
245 {
246 let res = p.hostnames.get(&ip);
247 assert_eq!(res.is_some(), true);
248
249 let res = res.unwrap();
250
251 assert_eq!(res.hostnames, host);
252 }
253
254 return;
255}
256
257#[test]
258fn test_parse_host_file()
259{
260 let ip0:IpAddr = "127.0.0.1".parse().unwrap();
261 let ip1:IpAddr = "127.0.1.1".parse().unwrap();
262 let ip2:IpAddr = "::1".parse().unwrap();
263 let ip3:IpAddr = "ff02::1".parse().unwrap();
264 let ip4:IpAddr = "ff02::2".parse().unwrap();
265
266 let hosts0: Vec<&'static str> = vec!["localhost"];
267 let hosts1: Vec<&'static str> = vec!["debian-laptop"];
268 let hosts2: Vec<&'static str> = vec!["localhost", "ip6-localhost", "ip6-loopback"];
269 let hosts3: Vec<&'static str> = vec!["ip6-allnodes"];
270 let hosts4: Vec<&'static str> = vec!["ip6-allrouters"];
271
272 let ip_list =
273 vec![
274 (ip0, hosts0),
275 (ip1, hosts1),
276 (ip2, hosts2),
277 (ip3, hosts3),
278 (ip4, hosts4)
279 ];
280
281 let test =
282 "127.0.0.1 localhost
283 127.0.1.1 debian-laptop
284
285 # The following lines are desirable for IPv6 capable hosts
286 ::1 localhost ip6-localhost ip6-loopback
287 ff02::1 ip6-allnodes
288 ff02::2 ip6-allrouters".to_string();
289
290 let mut writer = Writer::new();
291
292 let p = HostConfig::parse_host_file_internal(test, &mut writer);
293 assert_eq!(p.is_ok(), true, "{}", p.err().unwrap());
294
295 let p = p.unwrap();
296
297 for (ip, host) in ip_list
298 {
299 let res = p.hostnames.get(&ip);
300 assert_eq!(res.is_some(), true);
301
302 let res = res.unwrap();
303
304 assert_eq!(res.hostnames, host);
305 }
306}
307
308#[test]
309fn test_parse_host_file_2()
310{
311 let ip0:IpAddr = "127.0.0.1".parse().unwrap();
312 let ip1:IpAddr = "127.0.1.1".parse().unwrap();
313 let ip2:IpAddr = "::1".parse().unwrap();
314 let ip3:IpAddr = "ff02::1".parse().unwrap();
315 let ip4:IpAddr = "ff02::2".parse().unwrap();
316
317 let hosts0: Vec<&'static str> = vec!["localhost", "localdomain", "domain.local"];
318 let hosts1: Vec<&'static str> = vec!["debian-laptop", "test123.domain.tld"];
319 let hosts2: Vec<&'static str> = vec!["localhost", "ip6-localhost", "ip6-loopback"];
320 let hosts3: Vec<&'static str> = vec!["ip6-allnodes"];
321 let hosts4: Vec<&'static str> = vec!["ip6-allrouters"];
322
323
324 let ip_list =
325 vec![
326 (ip0, hosts0),
327 (ip1, hosts1),
328 (ip2, hosts2),
329 (ip3, hosts3),
330 (ip4, hosts4)
331 ];
332
333 let test =
334 "127.0.0.1 localhost localdomain domain.local
335 127.0.1.1 debian-laptop test123.domain.tld
336
337 # The following lines are desirable for IPv6 capable hosts
338 #
339 #
340 ::1 localhost ip6-localhost ip6-loopback
341 ff02::1 ip6-allnodes
342 ff02::2 ip6-allrouters
343 ".to_string();
344
345 let mut writer = Writer::new();
346
347 let p = HostConfig::parse_host_file_internal(test, &mut writer);
348 assert_eq!(p.is_ok(), true, "{}", p.err().unwrap());
349
350 let p = p.unwrap();
351
352 for (ip, host) in ip_list
353 {
354 let res = p.hostnames.get(&ip);
355 assert_eq!(res.is_some(), true);
356
357 let res = res.unwrap();
358
359 assert_eq!(res.hostnames, host);
360 }
361}
362