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 if let Some(net) = self.network() {
31 sub_checks.push(net.get_checks(None, params));
32 }
33
34 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 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 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 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 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 let domain = Domain::builder().ldh_name(ldh).build();
270 let rdap = domain.to_response();
271
272 let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
274
275 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 let domain = Domain::idn().unicode_name(unicode).build();
286 let rdap = domain.to_response();
287
288 let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
290
291 dbg!(&checks);
293 assert!(is_checked_item(Check::UnicodeNameInvalidDomain, &checks));
294 }
295
296 #[test]
297 fn test_check_for_ldh_unicode_mismatch() {
298 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 let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
307
308 dbg!(&checks);
310 assert!(is_checked_item(Check::LdhNameDoesNotMatchUnicode, &checks));
311 }
312
313 #[test]
314 fn test_delegation_signed_as_string() {
315 let secure_dns = serde_json::from_str::<SecureDns>(
317 r#"{
318 "delegationSigned": "true"
319 }"#,
320 )
321 .unwrap();
322
323 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 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 let secure_dns = serde_json::from_str::<SecureDns>(
345 r#"{
346 "delegationSigned": true
347 }"#,
348 )
349 .unwrap();
350
351 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 assert!(checks.items.is_empty());
366 }
367
368 #[test]
369 fn test_zone_signed_as_string() {
370 let secure_dns = serde_json::from_str::<SecureDns>(
372 r#"{
373 "zoneSigned": "false"
374 }"#,
375 )
376 .unwrap();
377
378 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 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 let secure_dns = serde_json::from_str::<SecureDns>(
400 r#"{
401 "zoneSigned": true
402 }"#,
403 )
404 .unwrap();
405
406 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 assert!(checks.items.is_empty());
421 }
422
423 #[test]
424 fn test_max_sig_life_as_string() {
425 let secure_dns = serde_json::from_str::<SecureDns>(
427 r#"{
428 "maxSigLife": "123"
429 }"#,
430 )
431 .unwrap();
432
433 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 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 let secure_dns = serde_json::from_str::<SecureDns>(
455 r#"{
456 "maxSigLife": 123
457 }"#,
458 )
459 .unwrap();
460
461 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 assert!(checks.items.is_empty());
476 }
477
478 #[test]
479 fn test_key_data_attributes_as_string() {
480 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 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 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 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 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 assert!(checks.items.is_empty());
544 }
545
546 #[test]
547 fn test_key_data_attributes_out_of_range() {
548 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 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 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 let secure_dns = serde_json::from_str::<SecureDns>(
588 r#"{
589 "keyData": [
590 {
591 "events": []
592 }
593 ]
594 }"#,
595 )
596 .unwrap();
597
598 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 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 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 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 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 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 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 assert!(checks.items.is_empty());
683 }
684
685 #[test]
686 fn test_ds_data_attributes_out_of_range() {
687 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 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 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 let secure_dns = serde_json::from_str::<SecureDns>(
728 r#"{
729 "dsData": [
730 {
731 "events": []
732 }
733 ]
734 }"#,
735 )
736 .unwrap();
737
738 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 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 let domain = Domain::builder()
761 .ldh_name("foo.example")
762 .entity(Entity::builder().handle("").build())
763 .build()
764 .to_response();
765
766 let checks = domain.get_checks(None, CheckParams::for_rdap(&domain));
768
769 assert!(contains_check(Check::HandleIsEmpty, &checks));
771 }
772
773 #[test]
774 fn test_domain_with_network_empty_handle() {
775 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 let checks = domain.get_checks(None, CheckParams::for_rdap(&domain));
790
791 assert!(contains_check(Check::HandleIsEmpty, &checks));
793 }
794
795 #[test]
796 fn test_domain_with_ns_empty_handle() {
797 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 let checks = domain.get_checks(None, CheckParams::for_rdap(&domain));
812
813 assert!(contains_check(Check::HandleIsEmpty, &checks));
815 }
816}