1use std::{any::TypeId, net::IpAddr, str::FromStr};
2
3use cidr::IpCidr;
4
5use crate::{prelude::Stringish, response::network::Network};
6
7use super::{string::StringCheck, Check, CheckParams, Checks, GetChecks, GetSubChecks};
8
9impl GetChecks for Network {
10 fn get_checks(&self, params: CheckParams) -> super::Checks {
11 let sub_checks = if params.do_subchecks {
12 let mut sub_checks: Vec<Checks> = self
13 .common
14 .get_sub_checks(params.from_parent(TypeId::of::<Self>()));
15 sub_checks.append(
16 &mut self
17 .object_common
18 .get_sub_checks(params.from_parent(TypeId::of::<Self>())),
19 );
20 if let Some(cidr0) = &self.cidr0_cidrs {
21 let check = match self
22 .network_type
23 .as_ref()
24 .unwrap_or(&Stringish::from("v4"))
25 .as_ref()
26 {
27 "v6" | "V6" => (Check::Cidr0V6PrefixIsAbsent, Check::Cidr0V6LengthIsAbsent),
28 _ => (Check::Cidr0V4PrefixIsAbsent, Check::Cidr0V4LengthIsAbsent),
29 };
30 cidr0.iter().for_each(|cidr| {
31 if cidr.prefix().is_none() {
32 sub_checks.push(Checks {
33 rdap_struct: super::RdapStructure::Cidr0,
34 items: vec![check.0.check_item()],
35 sub_checks: vec![],
36 })
37 };
38 if cidr.length().is_none() {
39 sub_checks.push(Checks {
40 rdap_struct: super::RdapStructure::Cidr0,
41 items: vec![check.1.check_item()],
42 sub_checks: vec![],
43 })
44 };
45 })
46 }
47 sub_checks
48 } else {
49 vec![]
50 };
51
52 let mut items = vec![];
53
54 if let Some(name) = &self.name {
55 if name.is_whitespace_or_empty() {
56 items.push(Check::NetworkOrAutnumNameIsEmpty.check_item())
57 }
58 if name.is_number() || name.is_bool() {
59 items.push(Check::NetworkOrAutnumNameIsNotString.check_item())
60 }
61 }
62
63 if let Some(network_type) = &self.network_type {
64 if network_type.is_whitespace_or_empty() {
65 items.push(Check::NetworkOrAutnumTypeIsEmpty.check_item())
66 }
67 if network_type.is_number() || network_type.is_bool() {
68 items.push(Check::NetworkOrAutnumTypeIsNotString.check_item())
69 }
70 }
71
72 if let Some(parent_handle) = &self.parent_handle {
73 if parent_handle.is_number() || parent_handle.is_bool() {
74 items.push(Check::ParentHandleIsNotString.check_item())
75 }
76 }
77
78 if let Some(country) = &self.country {
79 if country.is_number() || country.is_bool() {
80 items.push(Check::NetworkOrAutnumCountryIsNotString.check_item())
81 }
82 }
83
84 if self.start_address.is_none() || self.end_address.is_none() {
85 items.push(Check::IpAddressMissing.check_item())
86 }
87
88 if let Some(start_ip) = &self.start_address {
89 let start_addr = IpAddr::from_str(start_ip);
90 if start_addr.is_err() {
91 items.push(Check::IpAddressMalformed.check_item())
92 } else if self.end_address.is_some() {
93 let Ok(start_addr) = start_addr else {
94 panic!("ip result did not work")
95 };
96 let Some(end_ip) = &self.end_address else {
97 panic!("end address unwrap failed")
98 };
99 if let Ok(end_addr) = IpAddr::from_str(end_ip) {
100 if start_addr > end_addr {
101 items.push(Check::IpAddressEndBeforeStart.check_item())
102 }
103 if let Some(ip_version) = &self.ip_version {
104 if ip_version.is_number() || ip_version.is_bool() {
105 items.push(Check::IpVersionIsNotString.check_item())
106 }
107 if (**ip_version == *"v4" && (start_addr.is_ipv6() || end_addr.is_ipv6()))
108 || (**ip_version == *"v6"
109 && (start_addr.is_ipv4() || end_addr.is_ipv4()))
110 {
111 items.push(Check::IpAddressVersionMismatch.check_item())
112 } else if **ip_version != *"v4" && **ip_version != *"v6" {
113 items.push(Check::IpAddressMalformedVersion.check_item())
114 }
115 }
116 let this_network =
117 IpCidr::from_str("0.0.0.0/8").expect("incorrect this netowrk cidr");
118 if this_network.contains(&start_addr) && this_network.contains(&end_addr) {
119 items.push(Check::IpAddressThisNetwork.check_item())
120 }
121 let private_10 = IpCidr::from_str("10.0.0.0/8").expect("incorrect net 10 cidr");
122 let private_172 =
123 IpCidr::from_str("172.16.0.0/12").expect("incorrect net 172.16 cidr");
124 let private_192 =
125 IpCidr::from_str("192.168.0.0/16").expect("incorrect net 192.168 cidr");
126 if (private_10.contains(&start_addr) && private_10.contains(&end_addr))
127 || (private_172.contains(&start_addr) && private_172.contains(&end_addr))
128 || (private_192.contains(&start_addr) && private_192.contains(&end_addr))
129 {
130 items.push(Check::IpAddressPrivateUse.check_item())
131 }
132 let shared_nat =
133 IpCidr::from_str("100.64.0.0/10").expect("incorrect net 100 cidr");
134 if shared_nat.contains(&start_addr) && shared_nat.contains(&end_addr) {
135 items.push(Check::IpAddressSharedNat.check_item())
136 }
137 let loopback =
138 IpCidr::from_str("127.0.0.0/8").expect("incorrect loopback cidr");
139 if loopback.contains(&start_addr) && loopback.contains(&end_addr) {
140 items.push(Check::IpAddressLoopback.check_item())
141 }
142 let linklocal1 =
143 IpCidr::from_str("169.254.0.0/16").expect("incorrect linklocal1 cidr");
144 let linklocal2 =
145 IpCidr::from_str("fe80::/10").expect("incorrect linklocal2 cidr");
146 if (linklocal1.contains(&start_addr) && linklocal1.contains(&end_addr))
147 || (linklocal2.contains(&start_addr) && linklocal2.contains(&end_addr))
148 {
149 items.push(Check::IpAddressLinkLocal.check_item())
150 }
151 let uniquelocal =
152 IpCidr::from_str("fe80::/10").expect("incorrect unique local cidr");
153 if uniquelocal.contains(&start_addr) && uniquelocal.contains(&end_addr) {
154 items.push(Check::IpAddressUniqueLocal.check_item())
155 }
156 let doc1 = IpCidr::from_str("192.0.2.0/24").expect("incorrect doc1 cidr");
157 let doc2 = IpCidr::from_str("198.51.100.0/24").expect("incorrect doc2 cidr");
158 let doc3 = IpCidr::from_str("203.0.113.0/24").expect("incorrect doc3 cidr");
159 let doc4 = IpCidr::from_str("2001:db8::/32").expect("incorrect doc4 cidr");
160 if (doc1.contains(&start_addr) && doc1.contains(&end_addr))
161 || (doc2.contains(&start_addr) && doc2.contains(&end_addr))
162 || (doc3.contains(&start_addr) && doc3.contains(&end_addr))
163 || (doc4.contains(&start_addr) && doc4.contains(&end_addr))
164 {
165 items.push(Check::IpAddressDocumentationNet.check_item())
166 }
167 let reserved =
168 IpCidr::from_str("240.0.0.0/4").expect("incorrect reserved cidr");
169 if reserved.contains(&start_addr) && reserved.contains(&end_addr) {
170 items.push(Check::IpAddressLinkLocal.check_item())
171 }
172 }
173 }
174 }
175
176 if let Some(end_ip) = &self.end_address {
177 let addr = IpAddr::from_str(end_ip);
178 if addr.is_err() {
179 items.push(Check::IpAddressMalformed.check_item())
180 }
181 }
182
183 Checks {
184 rdap_struct: super::RdapStructure::IpNetwork,
185 items,
186 sub_checks,
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193
194 use rstest::rstest;
195
196 use crate::{
197 prelude::{Cidr0CidrPrefix, Numberish, ToResponse},
198 response::{
199 network::{Cidr0Cidr, Network},
200 RdapResponse,
201 },
202 };
203
204 use crate::check::{Check, CheckParams, GetChecks};
205
206 #[test]
207 fn check_network_with_empty_name() {
208 let mut network = Network::builder()
210 .cidr("10.0.0.0/8")
211 .build()
212 .expect("invalid ip cidr");
213 network.name = Some("".to_string().into());
214 let rdap = network.to_response();
215
216 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
218
219 dbg!(&checks);
221 assert!(checks
222 .items
223 .iter()
224 .any(|c| c.check == Check::NetworkOrAutnumNameIsEmpty));
225 }
226
227 #[test]
228 fn check_network_with_non_string_name() {
229 let json = r#"
231 {
232 "objectClassName" : "ip network",
233 "handle" : "XXXX-RIR",
234 "startAddress" : "2001:db8::",
235 "endAddress" : "2001:db8:0:ffff:ffff:ffff:ffff:ffff",
236 "ipVersion" : "v6",
237 "name": 1234,
238 "type" : "DIRECT ALLOCATION",
239 "country" : "AU",
240 "parentHandle" : "YYYY-RIR",
241 "status" : [ "active" ]
242 }
243 "#;
244 let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
245
246 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
248
249 assert!(checks
251 .items
252 .iter()
253 .any(|c| c.check == Check::NetworkOrAutnumNameIsNotString));
254 }
255
256 #[test]
257 fn check_network_with_empty_type() {
258 let mut network = Network::builder()
260 .cidr("10.0.0.0/8")
261 .build()
262 .expect("invalid ip cidr");
263 network.network_type = Some("".to_string().into());
264 let rdap = network.to_response();
265
266 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
268
269 dbg!(&checks);
271 assert!(checks
272 .items
273 .iter()
274 .any(|c| c.check == Check::NetworkOrAutnumTypeIsEmpty));
275 }
276
277 #[test]
278 fn check_network_with_non_string_type() {
279 let json = r#"
281 {
282 "objectClassName" : "ip network",
283 "handle" : "XXXX-RIR",
284 "startAddress" : "2001:db8::",
285 "endAddress" : "2001:db8:0:ffff:ffff:ffff:ffff:ffff",
286 "ipVersion" : "v6",
287 "name": "NET-RTR-1",
288 "type" : 1234,
289 "country" : "AU",
290 "parentHandle" : "YYYY-RIR",
291 "status" : [ "active" ]
292 }
293 "#;
294 let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
295
296 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
298
299 assert!(checks
301 .items
302 .iter()
303 .any(|c| c.check == Check::NetworkOrAutnumTypeIsNotString));
304 }
305
306 #[test]
307 fn check_network_with_no_start() {
308 let mut network = Network::builder()
310 .cidr("10.0.0.0/8")
311 .build()
312 .expect("invalid ip cidr");
313 network.start_address = None;
314 let rdap = network.to_response();
315
316 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
318
319 dbg!(&checks);
321 assert!(checks
322 .items
323 .iter()
324 .any(|c| c.check == Check::IpAddressMissing));
325 }
326
327 #[test]
328 fn check_network_with_no_end() {
329 let mut network = Network::builder()
331 .cidr("10.0.0.0/8")
332 .build()
333 .expect("invalid ip cidr");
334 network.end_address = None;
335 let rdap = network.to_response();
336
337 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
339
340 dbg!(&checks);
342 assert!(checks
343 .items
344 .iter()
345 .any(|c| c.check == Check::IpAddressMissing));
346 }
347
348 #[test]
349 fn check_network_with_bad_start() {
350 let mut network = Network::builder()
352 .cidr("10.0.0.0/8")
353 .build()
354 .expect("invalid ip cidr");
355 network.start_address = Some("____".to_string());
356 let rdap = network.to_response();
357
358 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
360
361 dbg!(&checks);
363 assert!(checks
364 .items
365 .iter()
366 .any(|c| c.check == Check::IpAddressMalformed));
367 }
368
369 #[test]
370 fn check_network_with_bad_end() {
371 let mut network = Network::builder()
373 .cidr("10.0.0.0/8")
374 .build()
375 .expect("invalid ip cidr");
376 network.end_address = Some("___".to_string());
377 let rdap = network.to_response();
378
379 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
381
382 dbg!(&checks);
384 assert!(checks
385 .items
386 .iter()
387 .any(|c| c.check == Check::IpAddressMalformed));
388 }
389
390 #[test]
391 fn check_network_with_end_before_start() {
392 let mut network = Network::builder()
394 .cidr("10.0.0.0/8")
395 .build()
396 .expect("invalid ip cidr");
397 let swap = network.end_address.clone();
398 network.end_address = network.start_address.clone();
399 network.start_address = swap;
400 let rdap = network.to_response();
401
402 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
404
405 dbg!(&checks);
407 assert!(checks
408 .items
409 .iter()
410 .any(|c| c.check == Check::IpAddressEndBeforeStart));
411 }
412
413 #[rstest]
414 #[case("10.0.0.0/8", "v6")]
415 #[case("2000::/64", "v4")]
416 fn check_network_with_ip_version(#[case] cidr: &str, #[case] version: &str) {
417 let mut network = Network::builder()
419 .cidr(cidr)
420 .build()
421 .expect("invalid ip cidr");
422 network.ip_version = Some(version.to_string().into());
423 let rdap = network.to_response();
424
425 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
427
428 dbg!(&checks);
430 assert!(checks
431 .items
432 .iter()
433 .any(|c| c.check == Check::IpAddressVersionMismatch));
434 }
435
436 #[rstest]
437 #[case("10.0.0.0/8", "__")]
438 #[case("2000::/64", "__")]
439 #[case("10.0.0.0/8", "")]
440 #[case("2000::/64", "")]
441 fn check_network_with_bad_ip_version(#[case] cidr: &str, #[case] version: &str) {
442 let mut network = Network::builder()
444 .cidr(cidr)
445 .build()
446 .expect("invalid ip cidr");
447 network.ip_version = Some(version.to_string().into());
448 let rdap = network.to_response();
449
450 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
452
453 dbg!(&checks);
455 assert!(checks
456 .items
457 .iter()
458 .any(|c| c.check == Check::IpAddressMalformedVersion));
459 }
460
461 #[test]
462 fn check_network_with_non_string_ip_version() {
463 let json = r#"
465 {
466 "objectClassName" : "ip network",
467 "handle" : "XXXX-RIR",
468 "startAddress" : "2001:db8::",
469 "endAddress" : "2001:db8:0:ffff:ffff:ffff:ffff:ffff",
470 "ipVersion" : 6,
471 "name": "NET-RTR-1",
472 "type" : "DIRECT ALLOCATION",
473 "country" : "AU",
474 "parentHandle" : "YYYY-RIR",
475 "status" : [ "active" ]
476 }
477 "#;
478 let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
479
480 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
482
483 assert!(checks
485 .items
486 .iter()
487 .any(|c| c.check == Check::IpVersionIsNotString));
488 }
489
490 #[test]
491 fn check_network_with_non_string_parent_handle() {
492 let json = r#"
494 {
495 "objectClassName" : "ip network",
496 "handle" : "XXXX-RIR",
497 "startAddress" : "2001:db8::",
498 "endAddress" : "2001:db8:0:ffff:ffff:ffff:ffff:ffff",
499 "ipVersion" : "v6",
500 "name": "NET-RTR-1",
501 "type" : "DIRECT ALLOCATION",
502 "country" : "AU",
503 "parentHandle" : 1234,
504 "status" : [ "active" ]
505 }
506 "#;
507 let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
508
509 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
511
512 assert!(checks
514 .items
515 .iter()
516 .any(|c| c.check == Check::ParentHandleIsNotString));
517 }
518
519 #[test]
520 fn check_network_with_non_string_country() {
521 let json = r#"
523 {
524 "objectClassName" : "ip network",
525 "handle" : "XXXX-RIR",
526 "startAddress" : "2001:db8::",
527 "endAddress" : "2001:db8:0:ffff:ffff:ffff:ffff:ffff",
528 "ipVersion" : "v6",
529 "name": "NET-RTR-1",
530 "type" : "DIRECT ALLOCATION",
531 "country" : 1234,
532 "parentHandle" : "YYYY-RIR",
533 "status" : [ "active" ]
534 }
535 "#;
536 let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
537
538 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
540
541 assert!(checks
543 .items
544 .iter()
545 .any(|c| c.check == Check::NetworkOrAutnumCountryIsNotString));
546 }
547
548 #[test]
549 fn check_cidr0_with_v4_prefixex() {
550 let network = Network::illegal()
552 .cidr0_cidrs(vec![Cidr0Cidr {
553 prefix: None,
554 length: Some(Numberish::<u8>::from(0)),
555 }])
556 .network_type("v4")
557 .build();
558 let rdap = network.to_response();
559
560 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
562
563 dbg!(&checks);
565 assert!(checks
566 .sub(crate::check::RdapStructure::Cidr0)
567 .expect("Cidr0")
568 .items
569 .iter()
570 .any(|c| c.check == Check::Cidr0V4PrefixIsAbsent));
571 }
572
573 #[test]
574 fn check_cidr0_with_v6_prefixex() {
575 let network = Network::illegal()
577 .cidr0_cidrs(vec![Cidr0Cidr {
578 prefix: None,
579 length: Some(Numberish::<u8>::from(0)),
580 }])
581 .network_type("v6")
582 .build();
583 let rdap = network.to_response();
584
585 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
587
588 dbg!(&checks);
590 assert!(checks
591 .sub(crate::check::RdapStructure::Cidr0)
592 .expect("Cidr0")
593 .items
594 .iter()
595 .any(|c| c.check == Check::Cidr0V6PrefixIsAbsent));
596 }
597
598 #[test]
599 fn check_cidr0_with_v4_length() {
600 let network = Network::illegal()
602 .cidr0_cidrs(vec![Cidr0Cidr {
603 prefix: Some(Cidr0CidrPrefix::V4Prefix("0.0.0.0".to_string())),
604 length: None,
605 }])
606 .network_type("v4")
607 .build();
608 let rdap = network.to_response();
609
610 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
612
613 dbg!(&checks);
615 assert!(checks
616 .sub(crate::check::RdapStructure::Cidr0)
617 .expect("Cidr0")
618 .items
619 .iter()
620 .any(|c| c.check == Check::Cidr0V4LengthIsAbsent));
621 }
622
623 #[test]
624 fn check_cidr0_with_v6_length() {
625 let network = Network::illegal()
627 .cidr0_cidrs(vec![Cidr0Cidr {
628 prefix: Some(Cidr0CidrPrefix::V6Prefix("0.0.0.0".to_string())),
629 length: None,
630 }])
631 .network_type("v6")
632 .build();
633 let rdap = network.to_response();
634
635 let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
637
638 dbg!(&checks);
640 assert!(checks
641 .sub(crate::check::RdapStructure::Cidr0)
642 .expect("Cidr0")
643 .items
644 .iter()
645 .any(|c| c.check == Check::Cidr0V6LengthIsAbsent));
646 }
647}