Skip to main content

icann_rdap_common/check/
domain.rs

1use std::any::TypeId;
2
3use crate::response::domain::{Domain, SecureDns};
4
5use super::{
6    string::StringCheck, Check, CheckItem, CheckParams, Checks, GetChecks, GetGroupChecks,
7};
8
9impl GetChecks for Domain {
10    fn get_checks(&self, index: Option<usize>, params: CheckParams) -> super::Checks {
11        let sub_checks = {
12            let mut sub_checks: Vec<Checks> = vec![];
13            sub_checks.append(&mut GetGroupChecks::get_group_checks(
14                &self.common,
15                params.from_parent(TypeId::of::<Self>()),
16            ));
17            sub_checks.append(
18                &mut self
19                    .object_common
20                    .get_group_checks(params.from_parent(TypeId::of::<Self>())),
21            );
22            if let Some(public_ids) = &self.public_ids {
23                sub_checks.push(public_ids.get_checks(None, params));
24            }
25            if let Some(secure_dns) = &self.secure_dns {
26                sub_checks.push(secure_dns.get_checks(None, params));
27            }
28
29            // network
30            if let Some(net) = self.network() {
31                sub_checks.push(net.get_checks(None, params));
32            }
33
34            // nameservers
35            for (i, ns) in self.nameservers().iter().enumerate() {
36                sub_checks.push(ns.get_checks(Some(i), params));
37            }
38
39            sub_checks
40        };
41
42        let mut items = vec![];
43
44        // check variants
45        if let Some(variants) = &self.variants {
46            let empty_count = variants
47                .iter()
48                .filter(|v| {
49                    v.relations.is_none() && v.idn_table.is_none() && v.variant_names.is_none()
50                })
51                .count();
52            if empty_count != 0 {
53                items.push(Check::VariantEmptyDomain.check_item());
54            };
55        };
56
57        // check ldh
58        if let Some(ldh) = &self.ldh_name {
59            if !ldh.is_ldh_domain_name() {
60                items.push(Check::LdhNameInvalid.check_item());
61            }
62            let name = ldh.trim_end_matches('.');
63            if name.eq("example")
64                || name.ends_with(".example")
65                || name.eq("example.com")
66                || name.ends_with(".example.com")
67                || name.eq("example.net")
68                || name.ends_with(".example.net")
69                || name.eq("example.org")
70                || name.ends_with(".example.org")
71            {
72                items.push(Check::LdhNameDocumentation.check_item())
73            }
74
75            // if there is also a unicodeName
76            if let Some(unicode_name) = &self.unicode_name {
77                let expected = idna::domain_to_ascii(unicode_name);
78                if let Ok(expected) = expected {
79                    if !expected.eq_ignore_ascii_case(ldh) {
80                        items.push(Check::LdhNameDoesNotMatchUnicode.check_item())
81                    }
82                }
83            }
84        }
85
86        // check unicode_name
87        if let Some(unicode_name) = &self.unicode_name {
88            if !unicode_name.is_unicode_domain_name() {
89                items.push(Check::UnicodeNameInvalidDomain.check_item());
90            }
91            let expected = idna::domain_to_ascii(unicode_name);
92            if expected.is_err() {
93                items.push(Check::UnicodeNameInvalidUnicode.check_item());
94            }
95        }
96
97        Checks {
98            rdap_struct: super::RdapStructure::Domain,
99            index,
100            items,
101            sub_checks,
102        }
103    }
104}
105
106impl GetChecks for SecureDns {
107    fn get_checks(&self, index: Option<usize>, params: CheckParams) -> Checks {
108        let mut items: Vec<CheckItem> = vec![];
109        if let Some(delegation_signed) = &self.delegation_signed {
110            if delegation_signed.is_string() {
111                items.push(Check::DelegationSignedIsString.check_item());
112            }
113        }
114        if let Some(zone_signed) = &self.zone_signed {
115            if zone_signed.is_string() {
116                items.push(Check::ZoneSignedIsString.check_item());
117            }
118        }
119        if let Some(max_sig_life) = &self.max_sig_life {
120            if max_sig_life.is_string() {
121                items.push(Check::MaxSigLifeIsString.check_item());
122            }
123        }
124
125        let mut sub_checks = vec![];
126        if let Some(key_data) = &self.key_data {
127            if key_data.is_empty() {
128                items.push(Check::KeyDataArrayIsEmpty.check_item());
129            } else {
130                for (i, key_datum) in key_data.iter().enumerate() {
131                    let mut items = vec![];
132                    if let Some(alg) = &key_datum.algorithm {
133                        if alg.is_string() {
134                            items.push(Check::KeyDatumAlgorithmIsString.check_item());
135                        }
136                        if alg.as_u8().is_none() {
137                            items.push(Check::KeyDatumAlgorithmIsOutOfRange.check_item());
138                        }
139                    }
140                    if let Some(flags) = &key_datum.flags {
141                        if flags.is_string() {
142                            items.push(Check::KeyDatumFlagsIsString.check_item());
143                        }
144                        if flags.as_u16().is_none() {
145                            items.push(Check::KeyDatumFlagsIsOutOfRange.check_item());
146                        }
147                    }
148                    if let Some(protocol) = &key_datum.protocol {
149                        if protocol.is_string() {
150                            items.push(Check::KeyDatumProtocolIsString.check_item());
151                        }
152                        if protocol.as_u8().is_none() {
153                            items.push(Check::KeyDatumProtocolIsOutOfRange.check_item());
154                        }
155                    }
156                    let mut event_checks = vec![];
157                    if let Some(events) = &key_datum.events {
158                        if events.is_empty() {
159                            sub_checks.push(Checks {
160                                rdap_struct: super::RdapStructure::Events,
161                                index: None,
162                                items: vec![Check::EventsArrayIsEmpty.check_item()],
163                                sub_checks: vec![],
164                            })
165                        } else {
166                            events.iter().enumerate().for_each(|(i, e)| {
167                                event_checks.push(e.get_checks(Some(i), params));
168                            });
169                        }
170                    }
171                    sub_checks.push(Checks {
172                        rdap_struct: super::RdapStructure::KeyData,
173                        index: Some(i),
174                        items,
175                        sub_checks: event_checks,
176                    });
177                }
178            }
179        }
180
181        if let Some(ds_data) = &self.ds_data {
182            if ds_data.is_empty() {
183                items.push(Check::DsDataArrayIsEmpty.check_item());
184            } else {
185                for (i, ds_datum) in ds_data.iter().enumerate() {
186                    let mut items = vec![];
187                    if let Some(alg) = &ds_datum.algorithm {
188                        if alg.is_string() {
189                            items.push(Check::DsDatumAlgorithmIsString.check_item());
190                        }
191                        if alg.as_u8().is_none() {
192                            items.push(Check::DsDatumAlgorithmIsOutOfRange.check_item());
193                        }
194                    }
195                    if let Some(key_tag) = &ds_datum.key_tag {
196                        if key_tag.is_string() {
197                            items.push(Check::DsDatumKeyTagIsString.check_item());
198                        }
199                        if key_tag.as_u32().is_none() {
200                            items.push(Check::DsDatumKeyTagIsOutOfRange.check_item());
201                        }
202                    }
203                    if let Some(digest_type) = &ds_datum.digest_type {
204                        if digest_type.is_string() {
205                            items.push(Check::DsDatumDigestTypeIsString.check_item());
206                        }
207                        if digest_type.as_u8().is_none() {
208                            items.push(Check::DsDatumDigestTypeIsOutOfRange.check_item());
209                        }
210                    }
211                    let mut event_checks = vec![];
212                    if let Some(events) = &ds_datum.events {
213                        if events.is_empty() {
214                            sub_checks.push(Checks {
215                                rdap_struct: super::RdapStructure::Events,
216                                index: None,
217                                items: vec![Check::EventsArrayIsEmpty.check_item()],
218                                sub_checks: vec![],
219                            })
220                        } else {
221                            events.iter().enumerate().for_each(|(i, e)| {
222                                event_checks.push(e.get_checks(Some(i), params));
223                            });
224                        }
225                    }
226                    sub_checks.push(Checks {
227                        rdap_struct: super::RdapStructure::DsData,
228                        index: Some(i),
229                        items,
230                        sub_checks: event_checks,
231                    });
232                }
233            }
234        }
235
236        Checks {
237            rdap_struct: super::RdapStructure::SecureDns,
238            index,
239            items,
240            sub_checks,
241        }
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use std::any::TypeId;
248
249    use {
250        crate::{
251            check::is_checked_item,
252            prelude::ToResponse,
253            response::domain::{Domain, SecureDns},
254        },
255        rstest::rstest,
256    };
257
258    use crate::{
259        check::{contains_check, Check, CheckParams, GetChecks},
260        prelude::{Entity, Nameserver, Network},
261    };
262
263    #[rstest]
264    #[case("")]
265    #[case("  ")]
266    #[case("_.")]
267    fn test_check_for_bad_ldh(#[case] ldh: &str) {
268        // GIVEN
269        let domain = Domain::builder().ldh_name(ldh).build();
270        let rdap = domain.to_response();
271
272        // WHEN
273        let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
274
275        // THEN
276        dbg!(&checks);
277        assert!(is_checked_item(Check::LdhNameInvalid, &checks));
278    }
279
280    #[rstest]
281    #[case("")]
282    #[case("  ")]
283    fn test_check_for_bad_unicode(#[case] unicode: &str) {
284        // GIVEN
285        let domain = Domain::idn().unicode_name(unicode).build();
286        let rdap = domain.to_response();
287
288        // WHEN
289        let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
290
291        // THEN
292        dbg!(&checks);
293        assert!(is_checked_item(Check::UnicodeNameInvalidDomain, &checks));
294    }
295
296    #[test]
297    fn test_check_for_ldh_unicode_mismatch() {
298        // GIVEN
299        let domain = Domain::idn()
300            .unicode_name("foo.com")
301            .ldh_name("xn--foo.com")
302            .build();
303        let rdap = domain.to_response();
304
305        // WHEN
306        let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
307
308        // THEN
309        dbg!(&checks);
310        assert!(is_checked_item(Check::LdhNameDoesNotMatchUnicode, &checks));
311    }
312
313    #[test]
314    fn test_delegation_signed_as_string() {
315        // GIVEN
316        let secure_dns = serde_json::from_str::<SecureDns>(
317            r#"{
318                "delegationSigned": "true"
319            }"#,
320        )
321        .unwrap();
322
323        // WHEN
324        let checks = secure_dns.get_checks(
325            None,
326            CheckParams {
327                root: &Domain::builder()
328                    .ldh_name("example.com")
329                    .build()
330                    .to_response(),
331                parent_type: TypeId::of::<SecureDns>(),
332                allow_unreg_ext: false,
333            },
334        );
335
336        // THEN
337        assert_eq!(checks.items.len(), 1);
338        assert!(is_checked_item(Check::DelegationSignedIsString, &checks));
339    }
340
341    #[test]
342    fn test_delegation_signed_as_bool() {
343        // GIVEN
344        let secure_dns = serde_json::from_str::<SecureDns>(
345            r#"{
346                "delegationSigned": true
347            }"#,
348        )
349        .unwrap();
350
351        // WHEN
352        let checks = secure_dns.get_checks(
353            None,
354            CheckParams {
355                root: &Domain::builder()
356                    .ldh_name("example.com")
357                    .build()
358                    .to_response(),
359                parent_type: TypeId::of::<SecureDns>(),
360                allow_unreg_ext: false,
361            },
362        );
363
364        // THEN
365        assert!(checks.items.is_empty());
366    }
367
368    #[test]
369    fn test_zone_signed_as_string() {
370        // GIVEN
371        let secure_dns = serde_json::from_str::<SecureDns>(
372            r#"{
373                "zoneSigned": "false"
374            }"#,
375        )
376        .unwrap();
377
378        // WHEN
379        let checks = secure_dns.get_checks(
380            None,
381            CheckParams {
382                root: &Domain::builder()
383                    .ldh_name("example.com")
384                    .build()
385                    .to_response(),
386                parent_type: TypeId::of::<SecureDns>(),
387                allow_unreg_ext: false,
388            },
389        );
390
391        // THEN
392        assert_eq!(checks.items.len(), 1);
393        assert!(is_checked_item(Check::ZoneSignedIsString, &checks));
394    }
395
396    #[test]
397    fn test_zone_signed_as_bool() {
398        // GIVEN
399        let secure_dns = serde_json::from_str::<SecureDns>(
400            r#"{
401                "zoneSigned": true
402            }"#,
403        )
404        .unwrap();
405
406        // WHEN
407        let checks = secure_dns.get_checks(
408            None,
409            CheckParams {
410                root: &Domain::builder()
411                    .ldh_name("example.com")
412                    .build()
413                    .to_response(),
414                parent_type: TypeId::of::<SecureDns>(),
415                allow_unreg_ext: false,
416            },
417        );
418
419        // THEN
420        assert!(checks.items.is_empty());
421    }
422
423    #[test]
424    fn test_max_sig_life_as_string() {
425        // GIVEN
426        let secure_dns = serde_json::from_str::<SecureDns>(
427            r#"{
428                "maxSigLife": "123"
429            }"#,
430        )
431        .unwrap();
432
433        // WHEN
434        let checks = secure_dns.get_checks(
435            None,
436            CheckParams {
437                root: &Domain::builder()
438                    .ldh_name("example.com")
439                    .build()
440                    .to_response(),
441                parent_type: TypeId::of::<SecureDns>(),
442                allow_unreg_ext: false,
443            },
444        );
445
446        // THEN
447        assert_eq!(checks.items.len(), 1);
448        assert!(is_checked_item(Check::MaxSigLifeIsString, &checks));
449    }
450
451    #[test]
452    fn test_max_sig_life_as_number() {
453        // GIVEN
454        let secure_dns = serde_json::from_str::<SecureDns>(
455            r#"{
456                "maxSigLife": 123
457            }"#,
458        )
459        .unwrap();
460
461        // WHEN
462        let checks = secure_dns.get_checks(
463            None,
464            CheckParams {
465                root: &Domain::builder()
466                    .ldh_name("example.com")
467                    .build()
468                    .to_response(),
469                parent_type: TypeId::of::<SecureDns>(),
470                allow_unreg_ext: false,
471            },
472        );
473
474        // THEN
475        assert!(checks.items.is_empty());
476    }
477
478    #[test]
479    fn test_key_data_attributes_as_string() {
480        // GIVEN
481        let secure_dns = serde_json::from_str::<SecureDns>(
482            r#"{
483                "keyData": [
484                    {
485                        "algorithm": "13",
486                        "flags": "13",
487                        "protocol": "13"
488                    }
489                ]
490            }"#,
491        )
492        .unwrap();
493
494        // WHEN
495        let checks = secure_dns.get_checks(
496            None,
497            CheckParams {
498                root: &Domain::builder()
499                    .ldh_name("example.com")
500                    .build()
501                    .to_response(),
502                parent_type: TypeId::of::<SecureDns>(),
503                allow_unreg_ext: false,
504            },
505        );
506
507        // THEN
508        assert!(contains_check(Check::KeyDatumAlgorithmIsString, &checks));
509        assert!(contains_check(Check::KeyDatumFlagsIsString, &checks));
510        assert!(contains_check(Check::KeyDatumProtocolIsString, &checks));
511    }
512
513    #[test]
514    fn test_key_data_attributes_as_number() {
515        // GIVEN
516        let secure_dns = serde_json::from_str::<SecureDns>(
517            r#"{
518                "keyData": [
519                    {
520                        "algorithm": 13,
521                        "flags": 13,
522                        "protocol": 13
523                    }
524                ]
525            }"#,
526        )
527        .unwrap();
528
529        // WHEN
530        let checks = secure_dns.get_checks(
531            None,
532            CheckParams {
533                root: &Domain::builder()
534                    .ldh_name("example.com")
535                    .build()
536                    .to_response(),
537                parent_type: TypeId::of::<SecureDns>(),
538                allow_unreg_ext: false,
539            },
540        );
541
542        // THEN
543        assert!(checks.items.is_empty());
544    }
545
546    #[test]
547    fn test_key_data_attributes_out_of_range() {
548        // GIVEN
549        let secure_dns = serde_json::from_str::<SecureDns>(
550            r#"{
551                "keyData": [
552                    {
553                        "algorithm": 1300,
554                        "flags": 130000,
555                        "protocol": 1300
556                    }
557                ]
558            }"#,
559        )
560        .unwrap();
561
562        // WHEN
563        let checks = secure_dns.get_checks(
564            None,
565            CheckParams {
566                root: &Domain::builder()
567                    .ldh_name("example.com")
568                    .build()
569                    .to_response(),
570                parent_type: TypeId::of::<SecureDns>(),
571                allow_unreg_ext: false,
572            },
573        );
574
575        // THEN
576        assert!(contains_check(
577            Check::KeyDatumAlgorithmIsOutOfRange,
578            &checks
579        ));
580        assert!(contains_check(Check::KeyDatumFlagsIsOutOfRange, &checks));
581        assert!(contains_check(Check::KeyDatumProtocolIsOutOfRange, &checks));
582    }
583
584    #[test]
585    fn test_key_datum_events_array_is_empty() {
586        // GIVEN
587        let secure_dns = serde_json::from_str::<SecureDns>(
588            r#"{
589                "keyData": [
590                    {
591                        "events": []
592                    }
593                ]
594            }"#,
595        )
596        .unwrap();
597
598        // WHEN
599        let checks = secure_dns.get_checks(
600            None,
601            CheckParams {
602                root: &Domain::builder()
603                    .ldh_name("example.com")
604                    .build()
605                    .to_response(),
606                parent_type: TypeId::of::<SecureDns>(),
607                allow_unreg_ext: false,
608            },
609        );
610
611        // THEN
612        dbg!(&checks);
613        let event_checks = checks.sub_checks.first().unwrap();
614        assert!(is_checked_item(Check::EventsArrayIsEmpty, event_checks));
615    }
616
617    #[test]
618    fn test_ds_data_attributes_as_string() {
619        // GIVEN
620        let secure_dns = serde_json::from_str::<SecureDns>(
621            r#"{
622                "dsData": [
623                    {
624                        "algorithm": "13",
625                        "keyTag": "13",
626                        "digestType": "13"
627                    }
628                ]
629            }"#,
630        )
631        .unwrap();
632
633        // WHEN
634        let checks = secure_dns.get_checks(
635            None,
636            CheckParams {
637                root: &Domain::builder()
638                    .ldh_name("example.com")
639                    .build()
640                    .to_response(),
641                parent_type: TypeId::of::<SecureDns>(),
642                allow_unreg_ext: false,
643            },
644        );
645
646        // THEN
647        assert!(contains_check(Check::DsDatumAlgorithmIsString, &checks));
648        assert!(contains_check(Check::DsDatumKeyTagIsString, &checks));
649        assert!(contains_check(Check::DsDatumDigestTypeIsString, &checks));
650    }
651
652    #[test]
653    fn test_ds_data_attributes_as_number() {
654        // GIVEN
655        let secure_dns = serde_json::from_str::<SecureDns>(
656            r#"{
657                "dsData": [
658                    {
659                        "algorithm": 13,
660                        "keyTag": 13,
661                        "digestType": 13
662                    }
663                ]
664            }"#,
665        )
666        .unwrap();
667
668        // WHEN
669        let checks = secure_dns.get_checks(
670            None,
671            CheckParams {
672                root: &Domain::builder()
673                    .ldh_name("example.com")
674                    .build()
675                    .to_response(),
676                parent_type: TypeId::of::<SecureDns>(),
677                allow_unreg_ext: false,
678            },
679        );
680
681        // THEN
682        assert!(checks.items.is_empty());
683    }
684
685    #[test]
686    fn test_ds_data_attributes_out_of_range() {
687        // GIVEN
688        let secure_dns = serde_json::from_str::<SecureDns>(
689            r#"{
690                "dsData": [
691                    {
692                        "algorithm": 1300,
693                        "keyTag": 13000000000,
694                        "digestType": 1300
695                    }
696                ]
697            }"#,
698        )
699        .unwrap();
700
701        // WHEN
702        let checks = secure_dns.get_checks(
703            None,
704            CheckParams {
705                root: &Domain::builder()
706                    .ldh_name("example.com")
707                    .build()
708                    .to_response(),
709                parent_type: TypeId::of::<SecureDns>(),
710                allow_unreg_ext: false,
711            },
712        );
713
714        // THEN
715        dbg!(&checks);
716        assert!(contains_check(Check::DsDatumAlgorithmIsOutOfRange, &checks));
717        assert!(contains_check(Check::DsDatumKeyTagIsOutOfRange, &checks));
718        assert!(contains_check(
719            Check::DsDatumDigestTypeIsOutOfRange,
720            &checks
721        ));
722    }
723
724    #[test]
725    fn test_ds_datum_events_array_is_empty() {
726        // GIVEN
727        let secure_dns = serde_json::from_str::<SecureDns>(
728            r#"{
729                "dsData": [
730                    {
731                        "events": []
732                    }
733                ]
734            }"#,
735        )
736        .unwrap();
737
738        // WHEN
739        let checks = secure_dns.get_checks(
740            None,
741            CheckParams {
742                root: &Domain::builder()
743                    .ldh_name("example.com")
744                    .build()
745                    .to_response(),
746                parent_type: TypeId::of::<SecureDns>(),
747                allow_unreg_ext: false,
748            },
749        );
750
751        // THEN
752        dbg!(&checks);
753        let event_checks = checks.sub_checks.first().unwrap();
754        assert!(is_checked_item(Check::EventsArrayIsEmpty, event_checks));
755    }
756
757    #[test]
758    fn test_domain_with_entity_empty_handle() {
759        // GIVEN
760        let domain = Domain::builder()
761            .ldh_name("foo.example")
762            .entity(Entity::builder().handle("").build())
763            .build()
764            .to_response();
765
766        // WHEN
767        let checks = domain.get_checks(None, CheckParams::for_rdap(&domain));
768
769        // THEN
770        assert!(contains_check(Check::HandleIsEmpty, &checks));
771    }
772
773    #[test]
774    fn test_domain_with_network_empty_handle() {
775        // GIVEN
776        let domain = Domain::builder()
777            .ldh_name("foo.example")
778            .network(
779                Network::builder()
780                    .cidr("10.0.0.0/8")
781                    .handle("")
782                    .build()
783                    .unwrap(),
784            )
785            .build()
786            .to_response();
787
788        // WHEN
789        let checks = domain.get_checks(None, CheckParams::for_rdap(&domain));
790
791        // THEN
792        assert!(contains_check(Check::HandleIsEmpty, &checks));
793    }
794
795    #[test]
796    fn test_domain_with_ns_empty_handle() {
797        // GIVEN
798        let domain = Domain::builder()
799            .ldh_name("foo.example")
800            .nameserver(
801                Nameserver::builder()
802                    .ldh_name("ns.foo.example")
803                    .handle("")
804                    .build()
805                    .unwrap(),
806            )
807            .build()
808            .to_response();
809
810        // WHEN
811        let checks = domain.get_checks(None, CheckParams::for_rdap(&domain));
812
813        // THEN
814        assert!(contains_check(Check::HandleIsEmpty, &checks));
815    }
816}