1use std::collections::{BTreeSet, HashMap};
18use std::convert::TryFrom;
19use std::marker::PhantomData;
20use std::net::IpAddr;
21use std::sync::Arc;
22use std::time::Duration;
23use std::time::Instant;
24
25use async_recursion::async_recursion;
26
27
28use crate::a_sync::caches::CachesController;
29
30#[cfg(feature = "built_in_async")]
31use crate::a_sync::interface::MutexedCaches;
32#[cfg(not(feature = "built_in_async"))]
33use crate::a_sync::interface::MutexedCaches;
34
35#[cfg(feature = "built_in_async")]
36use crate::a_sync::{IoInterf, SocketBase};
37
38#[cfg(feature = "built_in_async")]
39use crate::a_sync::TokioInterf;
40
41use crate::a_sync::SocketTaps;
42use crate::cfg_resolv_parser::{ConfigEntryTls, ResolveConfEntry, ResolveConfigFamily};
43use crate::common::{CDdnsGlobals, DnsRequestAnswer, DnsRequestHeader};
44use crate::{error::*, DnsResponsePayload, QDnsQueryResult};
45use crate::query::QDnsQuery;
46use crate::{write_error, internal_error};
47use crate::query_private::QDnsReq;
48
49use super::network::{NetworkTapType, SocketTap};
50use super::{QDnsName, QType, QuerySetup, ResolveConfig};
51
52#[cfg(feature = "built_in_async")]
83#[derive(Clone, Debug)]
84pub struct QDns<LOC: Sync + Send = SocketBase, TAP: SocketTaps<LOC> = SocketBase, MC: MutexedCaches = IoInterf>
85{
86 resolvers: Arc<ResolveConfig>,
88
89 ordered_req_list: Vec<QDnsReq>,
91
92 opts: QuerySetup,
94
95 cache: Arc<CachesController<MC>>,
97
98 _tap: PhantomData<TAP>,
100
101 _loc: PhantomData<LOC>
103}
104
105#[cfg(feature = "use_async_tokio")]
106impl QDns<SocketBase>
107{
108 #[inline]
134 pub async
135 fn builtin_make_empty(resolvers: Option<Arc<ResolveConfig>>, opts: QuerySetup, cache: Arc<CachesController<TokioInterf>>) -> CDnsResult<QDns<SocketBase, SocketBase, TokioInterf>>
136 {
137 return QDns::<_>::make_empty(resolvers, opts, cache).await;
138 }
139
140 #[inline]
148 pub
149 fn buildin_add_request<R>(&mut self, qtype: QType, req_name: R) -> CDnsResult<()>
150 where
151 R: TryInto<QDnsName, Error = CDnsError>,
152 {
153 let qr = QDnsReq::new_into(req_name, qtype)?;
154
155 self.ordered_req_list.push(qr);
156
157 return Ok(());
158 }
159
160 pub async
187 fn buildin_make_a_aaaa_request<R: AsRef<str>>(resolvers_opt: Option<Arc<ResolveConfig>>, req_name: R,
188 opts: QuerySetup, cache: Arc<CachesController<TokioInterf>>) -> CDnsResult<Self>
189 {
190 return QDns::<_>::make_a_aaaa_request(resolvers_opt, req_name, opts, cache).await;
191 }
192}
193
194#[cfg(not(feature = "built_in_async"))]
224#[derive(Clone, Debug)]
225pub struct QDns<LOC: Sync + Send, TAP: SocketTaps<LOC>, MC: MutexedCaches>
226{
227 resolvers: Arc<ResolveConfig>,
229
230 ordered_req_list: Vec<QDnsReq>,
232
233 opts: QuerySetup,
235
236 cache: Arc<CachesController<MC>>,
238
239 _tap: PhantomData<TAP>,
241
242 _loc: PhantomData<LOC>
244}
245
246impl<LOC: Sync + Send, TAP: SocketTaps<LOC>, MC: MutexedCaches> QDns<LOC, TAP, MC>
247{
248 pub async
271 fn make_empty(resolvers: Option<Arc<ResolveConfig>>, opts: QuerySetup, cache: Arc<CachesController<MC>>) -> CDnsResult<QDns<LOC, TAP, MC>>
272 {
273 return Ok(
274 Self
275 {
276 resolvers: resolvers.unwrap_or(cache.clone_resolve_list().await?),
277 ordered_req_list: Vec::new(),
278 opts: opts,
279 cache: cache,
280 _tap: PhantomData,
281 _loc: PhantomData
282 }
283 );
284 }
285
286 pub
294 fn add_request<R>(&mut self, qtype: QType, req_name: R) -> CDnsResult<()>
295 where
296 R: TryInto<QDnsName, Error = CDnsError>
297 {
298 let qr = QDnsReq::new_into(req_name, qtype)?;
299
300 self.ordered_req_list.push(qr);
301
302 return Ok(());
303 }
304
305 pub async
332 fn make_a_aaaa_request<R: AsRef<str>>(resolvers_opt: Option<Arc<ResolveConfig>>, req_name_ref: R,
333 opts: QuerySetup, cache: Arc<CachesController<MC>>) -> CDnsResult<Self>
334 {
335 let req_n = QDnsName::try_from(req_name_ref.as_ref())?;
336
337 let resolvers = resolvers_opt.unwrap_or(cache.clone_resolve_list().await?);
338
339 let reqs: Vec<QDnsReq> =
341 match resolvers.family
342 {
343 ResolveConfigFamily::INET4_INET6 =>
344 {
345 vec![
346 QDnsReq::new(req_n.clone(), QType::A),
347 QDnsReq::new(req_n, QType::AAAA),
348 ]
349 },
350 ResolveConfigFamily::INET6_INET4 =>
351 {
352 vec![
353 QDnsReq::new(req_n.clone(), QType::AAAA),
354 QDnsReq::new(req_n, QType::A),
355 ]
356 },
357 ResolveConfigFamily::INET6 =>
358 {
359 vec![
360 QDnsReq::new(req_n, QType::AAAA),
361 ]
362 },
363 ResolveConfigFamily::INET4 =>
364 {
365 vec![
366 QDnsReq::new(req_n, QType::A),
367 ]
368 }
369 _ =>
370 {
371 vec![
373 QDnsReq::new(req_n.clone(), QType::A),
374 QDnsReq::new(req_n, QType::AAAA),
375 ]
376 }
377 };
378
379
380
381 return Ok(
382 Self
383 {
384 resolvers: resolvers,
385 ordered_req_list: reqs,
386 opts: opts,
387 cache: cache,
388 _tap: PhantomData,
389 _loc: PhantomData
390 }
391 );
392 }
393
394 pub async
401 fn query(mut self) -> QDnsQueryResult
402 {
403 let now =
405 if self.opts.measure_time == true
406 {
407 Some(Instant::now())
408 }
409 else
410 {
411 None
412 };
413
414 if self.resolvers.lookup.is_file_first()
417 {
418 let mut query_res =
419 match self.lookup_file(now.as_ref()).await
420 {
421 Ok(file) =>
422 {
423 if file.is_empty() == false
424 {
425 self.ordered_req_list.retain(|req|
427 {
428 return !file.contains_dnsreq(req);
429 }
430 );
431 }
432
433 file
434 },
435 Err(e) =>
436 {
437 write_error!(e);
438
439 QDnsQueryResult::default()
440 }
441 };
442
443
444 if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_bind() == true
446 {
447 let res = self.process_request(now.as_ref()).await;
448
449 query_res.extend(res);
450 }
451
452 return query_res;
453 }
454 else
455 {
456 let mut dns_res = self.process_request(now.as_ref()).await;
457 if dns_res.is_empty() == false
458 {
459 self.ordered_req_list.retain(|req|
461 {
462 return !dns_res.contains_dnsreq(req);
463 }
464 );
465 }
466
467
468
469 if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_file() == true
470 {
471 match self.lookup_file(now.as_ref()).await
472 {
473 Ok(res) =>
474 {
475 dns_res.extend(res);
476 },
477 Err(e) =>
478 {
479 write_error!(e);
480 }
481 }
482 }
483
484 return dns_res;
485 }
486 }
487
488 fn get_timeout(&self) -> Duration
490 {
491 if let Some(timeout) = self.opts.timeout
492 {
493 return Duration::from_secs(timeout as u64);
494 }
495 else
496 {
497 return Duration::from_secs(self.resolvers.timeout as u64);
498 }
499 }
500
501 async
503 fn lookup_file(&mut self, now: Option<&Instant>) -> CDnsResult<QDnsQueryResult>
504 {
505 let mut dnsquries: QDnsQueryResult = QDnsQueryResult::default();
506
507 if self.opts.ign_hosts == false
509 {
510 let hlist = self.cache.clone_host_list().await?;
511
512 for req in self.ordered_req_list.iter()
513 {
514 match *req.get_type()
515 {
516 QType::A | QType::AAAA =>
517 {
518 let req_name = String::from(req.get_req_name());
519
520 let Some(host_name_ent) = hlist.search_by_fqdn(req.get_type(), req_name.as_str())
521 else { continue };
522
523 let Some(drp) = DnsResponsePayload::new_local(*req.get_type(), host_name_ent)
524 else { continue };
525
526 dnsquries.push(req.clone(), Ok(QDnsQuery::from_local(drp, now)));
528 },
529 QType::PTR =>
530 {
531 let Ok(ip) = IpAddr::try_from(req.get_req_name())
532 else { continue };
533
534 let Some(host_name_ent) = hlist.search_by_ip(&ip)
535 else { continue };
536
537 let Some(drp) = DnsResponsePayload::new_local(*req.get_type(), host_name_ent)
538 else { continue };
539
540 dnsquries.push(req.clone(), Ok(QDnsQuery::from_local(drp, now)));
541 },
542 _ =>
543 continue,
544 }
545 }
546
547 }
548
549 return Ok(dnsquries);
550 }
551
552 #[inline]
555 fn create_socket(&self, force_tcp: bool, resolver: Arc<ResolveConfEntry>) -> CDnsResult<Box<NetworkTapType<LOC>>>
556 {
557 if resolver.get_tls_type() != ConfigEntryTls::None
558 {
559 #[cfg(feature = "use_async_tokio_tls")]
560 return TAP::new_tls_socket(resolver, self.get_timeout());
561
562 #[cfg(not(feature = "use_async_tokio_tls"))]
563 internal_error!(CDnsErrorType::SocketNotSupported, "compiled without TLS support");
564 }
565 else if self.resolvers.option_flags.is_force_tcp() == true || force_tcp == true
566 {
567 return TAP::new_tcp_socket(resolver, self.get_timeout());
568 }
569 else
570 {
571 return TAP::new_udp_socket(resolver, self.get_timeout());
572 }
573 }
574
575 async
577 fn process_request(&mut self, now: Option<&Instant>) -> QDnsQueryResult
578 {
579 let mut responses: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
580
581 if self.resolvers.option_flags.is_no_parallel() == true
582 {
583 for req in self.ordered_req_list.iter()
584 {
585 let mut last_resp: Option<CDnsResult<QDnsQuery>> = None;
586
587 for resolver in self.resolvers.get_resolvers_iter()
588 {
589 match self.query_exec_seq(now, resolver.clone(), req, None).await
590 {
591 Ok(resp) =>
592 {
593 if resp.should_check_next_ns() == true
594 {
595 last_resp = Some(Ok(resp));
596
597 continue;
598 }
599 else
600 {
601 responses.push(req.clone(), Ok(resp));
602
603 let _ = last_resp.take();
604
605 break;
606 }
607 },
608 Err(e) =>
609 {
610 if last_resp.is_none() == true
611 {
612 last_resp = Some(Err(e));
613 }
614
615 continue;
616 }
617 }
618 } responses.push(req.clone(), last_resp.take().unwrap());
621 }}
623 else
624 {
625 for resolver in self.resolvers.get_resolvers_iter()
628 {
629 if self.ordered_req_list.is_empty() == true
630 {
631 break;
632 }
633
634 match self.query_exec_pipelined(now, resolver.clone(), None).await
635 {
636 Ok(resp) =>
637 {
638 for (qdns_res, qdns_que) in resp
639 {
640 if let Ok(ref resp) = qdns_que
641 {
642 if resp.should_check_next_ns() == false
643 {
644 self
645 .ordered_req_list
646 .retain(
647 |req_item|
648 req_item != &qdns_res
649 );
650 }
651 }
652
653 responses.push(qdns_res, qdns_que);
654 }
655 },
656 Err(e) =>
657 {
658 write_error!(e);
659
660 continue;
661 }
662 }
663 }
664 }
665
666 return responses;
667 }
668
669 #[async_recursion]
670 async
671 fn query_exec_pipelined(
672 &self,
673 now: Option<&Instant>,
674 resolver: Arc<ResolveConfEntry>,
675 requery: Option<HashMap<DnsRequestHeader<'async_recursion>, QDnsReq>>,
676 ) -> CDnsResult<QDnsQueryResult>
677 {
678 let force_tcp = self.resolvers.option_flags.is_force_tcp() || requery.is_some();
679
680 let mut query_headers: HashMap<DnsRequestHeader, QDnsReq> =
681 if let Some(requer) = requery
682 {
683 let pkts_ids = requer.iter().map(|q| q.0.get_id()).collect::<BTreeSet<u16>>();
684
685 requer
687 .into_iter()
688 .map(
689 |(mut qrr, qdr)|
690 {
691 loop
692 {
693 qrr.regenerate_id();
694
695 if pkts_ids.contains(&qrr.get_id()) == false
696 {
697 break;
698 }
699 }
700 (qrr, qdr)
701 })
702 .collect::<HashMap<DnsRequestHeader, QDnsReq>>()
703 }
704 else
705 {
706 let mut pkts_ids: BTreeSet<u16> = BTreeSet::new();
707
708 self
710 .ordered_req_list
711 .iter()
712 .map(
713 |query|
714 {
715
716 let mut drh_res = DnsRequestHeader::try_from(query);
717
718 loop
719 {
720 if let Ok(ref mut drh) = drh_res
721 {
722 if pkts_ids.contains(&drh.get_id()) == true
723 {
724 drh.regenerate_id();
725
726 continue;
727 }
728 else
729 {
730 pkts_ids.insert(drh.get_id());
731 break;
732 }
733 }
734 else
735 {
736 break;
737 }
738 }
739
740 drh_res.map(|dh| (dh, query.clone()))
741 }
742 )
743 .collect::<CDnsResult<HashMap<DnsRequestHeader, QDnsReq>>>()?
744 };
745
746 let mut tap = self.create_socket(force_tcp, resolver.clone())?;
748
749 tap.connect(CDdnsGlobals::get_tcp_conn_timeout()).await?;
750
751 for qh in query_headers.iter()
753 {
754 let pkt = qh.0.to_bytes(tap.should_append_len())?;
755
756 tap.send(pkt.as_slice()).await?;
757 }
758
759 let mut resp: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
760 let mut requery: HashMap<DnsRequestHeader, QDnsReq> = HashMap::new();
761 loop
764 {
765 if query_headers.len() == 0
766 {
767 break;
768 }
769
770 tap.poll_read().await?;
771
772 let ans = self.read_response(tap.as_mut()).await?;
773
774 let Some((query_header, qdnsreq)) = query_headers.remove_entry(&ans.req_header)
775 else
776 {
777 internal_error!(CDnsErrorType::IoError, "can not find response with request: {}", ans.req_header);
778 };
779
780 ans.verify(&query_header)?;
781
782 let qdns_resp = QDnsQuery::from_response(tap.get_remote_addr(), ans, now);
784
785 if let Ok(ref qdns) = qdns_resp
786 {
787 if qdns.get_status().should_try_tcp() == true && force_tcp == false
788 {
789 requery.insert(query_header, qdnsreq);
790 }
791 else
792 {
793 resp.push(qdnsreq, qdns_resp);
794 }
795 }
796 else
797 {
798 resp.push(qdnsreq, qdns_resp);
799 }
800 }
801
802 if requery.is_empty() == false
803 {
804 let res = self.query_exec_pipelined(now, resolver, Some(requery)).await?;
805
806 resp.extend(res);
807 }
808
809 return Ok(resp);
810 }
811
812 #[async_recursion]
814 async
815 fn query_exec_seq(
816 &self,
817 now: Option<&Instant>,
818 resolver: Arc<ResolveConfEntry>,
819 query: &QDnsReq,
820 requery: Option<DnsRequestHeader<'async_recursion>>,
821 ) -> CDnsResult<QDnsQuery>
822 {
823 let force_tcp = self.resolvers.option_flags.is_force_tcp() || requery.is_some();
824
825 let mut tap = self.create_socket(force_tcp, resolver.clone())?;
827
828 let query_header =
829 if let Some(mut requery) = requery
831 {
832 requery.regenerate_id();
833
834 requery
835 }
836 else
837 {
838 let drh_req = DnsRequestHeader::try_from(query)?;
839
840 drh_req
841 };
842
843 let res =
844 {
845 tap.connect(CDdnsGlobals::get_tcp_conn_timeout()).await?;
847
848 let pkt = query_header.to_bytes(tap.should_append_len())?;
850
851 tap.send(pkt.as_slice()).await?;
853
854 let ans = self.read_response(tap.as_mut()).await?;
855
856 ans.verify(&query_header)?;
857
858 let resp = QDnsQuery::from_response(tap.get_remote_addr(), ans, now)?;
860
861 Ok(resp)
862 };
863
864 if (res.is_ok() == true && res.as_ref().unwrap().status.should_try_tcp() == false) ||
865 (res.is_err() == true && force_tcp == true)
866 {
867 return res;
868 }
869
870
871 return
872 self.query_exec_seq(now, resolver.clone(), query, Some(query_header)).await;
873 }
874
875 async
877 fn read_response(&self, socktap: &mut dyn SocketTap<LOC>) -> CDnsResult<DnsRequestAnswer<'_>>
878 {
879 if socktap.is_tcp() == false
880 {
881 let mut rcvbuf = vec![0_u8; 1457];
882
883 let n = socktap.recv(rcvbuf.as_mut_slice()).await?;
885
886 return DnsRequestAnswer::parse(&rcvbuf); }
889 else
890 {
891 let mut pkg_pen: [u8; 2] = [0, 0];
892 let n = socktap.recv(&mut pkg_pen).await?;
893
894 if n == 0
895 {
896 internal_error!(CDnsErrorType::IoError, "tcp received zero len message!");
897 }
898 else if n != 2
899 {
900 internal_error!(CDnsErrorType::IoError, "tcp expected 2 bytes to be read!");
901 }
902
903 let ln = u16::from_be_bytes(pkg_pen);
904
905 let mut rcvbuf = vec![0_u8; ln as usize];
906
907 let mut n = socktap.recv(rcvbuf.as_mut_slice()).await?;
909
910 if n == 0
911 {
912 internal_error!(CDnsErrorType::IoError, "tcp received zero len message!");
913 }
914 else if n == 1
915 {
916 n = socktap.recv(&mut rcvbuf[1..]).await?;
917
918 if n == 0
919 {
920 internal_error!(CDnsErrorType::IoError, "tcp received zero len message again!");
921 }
922
923 n += 1;
924 }
925
926 return DnsRequestAnswer::parse(&rcvbuf);
927 }
928 }
929}
930
931#[cfg(feature = "use_async_tokio")]
932#[cfg(test)]
933mod tests
934{
935 use std::{net::IpAddr, sync::Arc};
936
937 use crate::{a_sync::{query::QDns, CachesController}, common::{byte2hexchar, ip2pkt, RecordPTR, RecordReader}, QDnsQueryRec, QType, QuerySetup};
938
939 #[tokio::test]
940 async fn test_ip2pkt()
941 {
942 use tokio::time::Instant;
943 use std::net::{IpAddr, Ipv4Addr};
944
945 let test = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
946
947 let now = Instant::now();
948
949 let res = ip2pkt(&test);
950
951 let elapsed = now.elapsed();
952 println!("Elapsed: {:.2?}", elapsed);
953
954 let ctrl = b"\x01\x38\x01\x38\x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\x61\x00";
955
956 assert_eq!(res.as_slice(), ctrl);
957 }
958
959
960 #[tokio::test]
961 async fn test_byte2hexchar()
962 {
963 assert_eq!(byte2hexchar(1), 0x31);
964 assert_eq!(byte2hexchar(9), 0x39);
965 assert_eq!(byte2hexchar(10), 'a' as u8);
966 assert_eq!(byte2hexchar(15), 'f' as u8);
967 }
968
969
970 #[tokio::test]
971 async fn reverse_lookup_test()
972 {
973 use tokio::time::Instant;
974
975 let cache = Arc::new(CachesController::new().await.unwrap());
976
977 let ipp: IpAddr = "8.8.8.8".parse().unwrap();
978 let mut query_setup = QuerySetup::default();
981 query_setup.set_measure_time(true);
982
983 let now = Instant::now();
984
985 let mut dns_req =
986 QDns::builtin_make_empty(None, query_setup, cache).await.unwrap();
987
988 dns_req.add_request(QType::PTR, ipp).unwrap();
989
990 let res = dns_req.query().await;
991
992 let elapsed = now.elapsed();
993 println!("Elapsed: {:.2?}", elapsed);
994
995 println!("{}", res);
996
997 assert_eq!(res.is_empty(), false);
998
999 let recs = res.collect_ok();
1000 let rec = &recs[0];
1001 assert_eq!(rec.status, QDnsQueryRec::Ok);
1003
1004 assert_eq!(rec.resp.len(), 1);
1005 assert_eq!(rec.resp[0].rdata, RecordPTR::wrap(RecordPTR{ fqdn: "dns.google".to_string() }));
1006 }
1007
1008 #[tokio::test]
1009 async fn reverse_lookup_hosts_test()
1010 {
1011 use tokio::time::Instant;
1012
1013 let cache = Arc::new(CachesController::new().await.unwrap());
1014
1015 let ipp: IpAddr = "127.0.0.1".parse().unwrap();
1016 let now = Instant::now();
1019
1020 let mut query_setup = QuerySetup::default();
1021 query_setup.set_measure_time(true);
1022
1023 let mut dns_req =
1024 QDns::builtin_make_empty(None, query_setup, cache).await.unwrap();
1025
1026 dns_req.add_request(QType::PTR, ipp).unwrap();
1027
1028 let res = dns_req.query().await;
1029
1030 let elapsed = now.elapsed();
1031 println!("Elapsed: {:.2?}", elapsed);
1032
1033 println!("{}", res);
1034
1035 assert_eq!(res.is_empty(), false);
1036
1037 let recs = res.collect_ok();
1038 let rec = &recs[0];
1039
1040 assert_eq!(rec.server.as_str(), "/etc/hosts");
1041 assert_eq!(rec.status, QDnsQueryRec::Ok);
1042
1043 assert_eq!(rec.resp.len(), 1);
1044 assert_eq!(rec.resp[0].rdata, RecordPTR::wrap(RecordPTR{ fqdn: "localhost".to_string() }));
1045 }
1046
1047
1048 #[tokio::test]
1049 async fn reverse_lookup_a()
1050 {
1051 use tokio::time::Instant;
1052
1053 let cache = Arc::new(CachesController::new().await.unwrap());
1056
1057 let mut query_setup = QuerySetup::default();
1058 query_setup.set_measure_time(true);
1059
1060
1061 let res =
1062 QDns::buildin_make_a_aaaa_request(None, "dns.google", query_setup, cache).await.unwrap();
1063
1064
1065 let now = Instant::now();
1066 let res = res.query().await;
1067
1068
1069 let elapsed = now.elapsed();
1070 println!("Elapsed: {:.2?}", elapsed);
1071
1072 println!("{}", res);
1073 }
1074
1075 #[tokio::test]
1076 async fn reverse_lookup_a_idn()
1077 {
1078 use std::time::Instant;
1079
1080 let cache = Arc::new(CachesController::new().await.unwrap());
1081
1082 let mut query_setup = QuerySetup::default();
1083 query_setup.set_measure_time(true);
1084
1085
1086
1087 let res =
1088 QDns
1089 ::buildin_make_a_aaaa_request(None, "законипорядок.бел", query_setup, cache).await.unwrap();
1090
1091 let now = Instant::now();
1092 let res = res.query().await;
1093
1094
1095 let elapsed = now.elapsed();
1096 println!("Elapsed: {:.2?}", elapsed);
1097
1098 println!("{}", res);
1102
1103 let ok: Vec<crate::QDnsQuery> = res.collect_ok();
1104 let name = ok[0].get_responses()[0].name.clone();
1105 let idn_name = ok[0].get_responses()[0].get_full_domain_name_with_idn_decode().unwrap();
1106
1107 println!("{} -> {}", name, idn_name);
1108 assert_eq!(name, "xn--80aihfjcshcbin9q.xn--90ais");
1109
1110 assert_eq!(idn_name, "законипорядок.бел");
1111 }
1112}