icann_rdap_common/check/
network.rs

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        // GIVEN
209        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        // WHEN
217        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
218
219        // THEN
220        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        // GIVEN
230        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        // WHEN
247        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
248
249        // THEN
250        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        // GIVEN
259        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        // WHEN
267        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
268
269        // THEN
270        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        // GIVEN
280        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        // WHEN
297        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
298
299        // THEN
300        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        // GIVEN
309        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        // WHEN
317        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
318
319        // THEN
320        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        // GIVEN
330        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        // WHEN
338        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
339
340        // THEN
341        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        // GIVEN
351        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        // WHEN
359        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
360
361        // THEN
362        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        // GIVEN
372        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        // WHEN
380        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
381
382        // THEN
383        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        // GIVEN
393        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        // WHEN
403        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
404
405        // THEN
406        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        // GIVEN
418        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        // WHEN
426        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
427
428        // THEN
429        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        // GIVEN
443        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        // WHEN
451        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
452
453        // THEN
454        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        // GIVEN
464        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        // WHEN
481        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
482
483        // THEN
484        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        // GIVEN
493        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        // WHEN
510        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
511
512        // THEN
513        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        // GIVEN
522        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        // WHEN
539        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
540
541        // THEN
542        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        // GIVEN
551        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        // WHEN
561        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
562
563        // THEN
564        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        // GIVEN
576        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        // WHEN
586        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
587
588        // THEN
589        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        // GIVEN
601        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        // WHEN
611        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
612
613        // THEN
614        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        // GIVEN
626        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        // WHEN
636        let checks = rdap.get_checks(CheckParams::for_rdap(&rdap));
637
638        // THEN
639        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}