cdns_rs/sync/query.rs
1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 *
4 * Copyright (C) 2020 Aleksandr Morozov
5 *
6 * Copyright 2025 Aleksandr Morozov
7 *
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 *
11 * You may not use this work except in compliance with the Licence.
12 *
13 * You may obtain a copy of the Licence at:
14 *
15 * https://joinup.ec.europa.eu/software/page/eupl
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24use std::collections::{BTreeSet, HashMap};
25use std::convert::TryFrom;
26use std::io::ErrorKind;
27use std::net::{IpAddr, SocketAddr, TcpStream, ToSocketAddrs, UdpSocket};
28use std::vec;
29use std::sync::Arc;
30use std::time::Duration;
31use std::time::Instant;
32
33use crate::cfg_resolv_parser::{ConfigEntryTls, ResolveConfEntry, ResolveConfig, ResolveConfigFamily};
34use crate::{error::*, write_error};
35use crate::query::{QDnsQuery, QDnsQueryResult, QuerySetup};
36use crate::sync::network::NetworkTap;
37use crate::internal_error;
38use crate::query_private::QDnsReq;
39use crate::common::*;
40
41use super::caches::CACHE;
42use super::network::{SocketTap, TcpHttpsConnection, TcpTlsConnection};
43
44/// A struct which implements the [ToSocketAddrs] to resolve A and AAAA
45/// directly to functions which argumnets are implementing [ToSocketAddrs]
46/// Available only for the `sync` version. Tokio has ToSocketAddrs `sealed`.
47#[derive(Clone, Debug)]
48pub enum QDnsSockerAddr
49{
50 Ip(SocketAddr),
51 Host(String, u16)
52}
53
54impl ToSocketAddrs for QDnsSockerAddr
55{
56 type Iter = vec::IntoIter<SocketAddr>;
57
58 fn to_socket_addrs(&self) -> std::io::Result<Self::Iter>
59 {
60 match self
61 {
62 QDnsSockerAddr::Ip(socket_addr) =>
63 return Ok(vec![socket_addr.clone()].into_iter()),
64 QDnsSockerAddr::Host(host, port) =>
65 {
66 let qdns =
67 QDns::make_a_aaaa_request(None, host.as_str(), QuerySetup::default())
68 .map_err(|e|
69 std::io::Error::new(ErrorKind::NotFound, CDnsSuperError::from(e))
70 )?;
71
72 let res =
73 qdns
74 .query()
75 .collect_ok()
76 .into_iter()
77 .map(|resp|
78 resp
79 .get_responses()
80 .iter()
81 .map(|r|
82 r.get_rdata().get_ip().map(|f| SocketAddr::new(f, *port))
83 )
84 .collect::<Vec<Option<SocketAddr>>>()
85 )
86 .flatten()
87 .filter(|p| p.is_some())
88 .map(|v| v.unwrap())
89 .collect::<Vec<SocketAddr>>();
90
91 return Ok( res.into_iter() );
92 }
93 }
94 }
95}
96
97
98impl QDnsSockerAddr
99{
100 /// Creates instance for A and AAAA records resolution from configured
101 /// in `/etc/resolv.conf` DNS servers. The instance performs the DNS resolve
102 /// when passed to functions which arguments implements [ToSocketAddrs].
103 ///
104 /// # Arguments
105 ///
106 /// * `host` - `domain`:`port` i.e example.com:443. The `ip``port` is also
107 /// possible and no DNS resolution will be performed.
108 pub
109 fn resolve<D>(host: D) -> std::io::Result<Self>
110 where D: AsRef<str>
111 {
112 if let Ok(addr) = host.as_ref().parse::<SocketAddr>()
113 {
114 return Ok(Self::Ip(addr));
115 }
116 else
117 {
118 let ref_host = host.as_ref();
119
120 let (domain, port) =
121 match ref_host.split_once(":")
122 {
123 Some((h, portno)) =>
124 {
125 let port: u16 =
126 portno
127 .parse()
128 .map_err(|e|
129 std::io::Error::new(ErrorKind::InvalidData, format!("{}", e))
130 )?;
131
132 (h, port)
133 },
134 None =>
135 return Err(std::io::Error::new(ErrorKind::InvalidData, "missing prot number"))
136 };
137
138 return Ok(Self::Host(domain.to_string(), port));
139 }
140 }
141
142 /// Same as `resolve` but port is provided separatly.
143 ///
144 /// # Arguments
145 ///
146 /// * `host` - `domain` name i.e example.com. The `ip`` is also
147 /// possible and no DNS resolution will be performed.
148 ///
149 /// * `port` - a port number to be added to the result.
150 pub
151 fn resolve_port<D>(host: D, port: u16) -> std::io::Result<Self>
152 where D: AsRef<str>
153 {
154 if let Ok(addr) = host.as_ref().parse::<IpAddr>()
155 {
156 return Ok(Self::Ip(SocketAddr::new(addr, port)));
157 }
158 else
159 {
160 return Ok(Self::Host(host.as_ref().to_string(), port));
161 }
162 }
163}
164
165/// A main instance which contains all common logic.
166#[derive(Clone, Debug)]
167pub struct QDns
168{
169 /// An instance of the parser /etc/resolv.conf or custom config
170 resolvers: Arc<ResolveConfig>,
171 /// A pre-ordered list of the requests, if more than one
172 ordered_req_list: Vec<QDnsReq>,
173 /// Override options
174 opts: QuerySetup,
175}
176
177impl QDns
178{
179 /// Initializes new empty storage for requests.
180 ///
181 /// # Arguments
182 ///
183 /// * `resolvers` - an [Arc] [ResolveConfig] which contains configuration i.e nameservers
184 ///
185 /// * `opts` - [QuerySetup] additional options or overrides. Use default() for default
186 /// values.
187 ///
188 /// # Returns
189 ///
190 /// Never panics. Returns Self.
191 pub
192 fn make_empty(resolvers: Option<Arc<ResolveConfig>>, opts: QuerySetup) -> CDnsResult<Self>
193 {
194 return Ok(
195 Self
196 {
197 resolvers: resolvers.unwrap_or(CACHE.clone_resolve_list()?),
198 ordered_req_list: Vec::new(),
199 opts: opts,
200 }
201 );
202 }
203
204 /// Adds new request to previously created empty storage for request with [QDns::make_empty].
205 ///
206 /// # Arguemnts
207 ///
208 /// * `qtype` - a [QType] type of the request
209 ///
210 /// * `req_name` - a [Into] [QDnsName] which is target. i.e 'localhost' or 'domain.tld'
211 ///
212 /// # Returns
213 ///
214 /// * [CDnsResult] - Ok with nothing in inner type
215 ///
216 /// * [CDnsResult] - Err with error description
217 pub
218 fn add_request(&mut self, qtype: QType, req_name: impl Into<QDnsName>)
219 {
220 let qr = QDnsReq::new_into(req_name, qtype);
221
222 self.ordered_req_list.push(qr);
223
224 return;
225 }
226
227 /// This is helper which makes for you an A, AAAA query. The order of A and AAAA and
228 /// which from both are allowed is defined in the [ResolveConfig].
229 ///
230 /// Use this function directly. Do not use [QDns::make_empty]
231 ///
232 /// # Arguments
233 ///
234 /// * `resolvers` - an [Option] value [Arc] [ResolveConfig] which can be used to
235 /// override the system's `resolv.conf`
236 ///
237 /// * `req_name` - a [Into] [QDnsName] which is target i.e 'localhost' or 'domain.tld'
238 ///
239 /// * `opts` - [QuerySetup] additional options or overrides. Use default() for default
240 /// values.
241 ///
242 /// # Returns
243 ///
244 /// A [CDnsResult] is returned;
245 ///
246 /// * [Result::Ok] - with Self as inner type
247 ///
248 /// * [Result::Err] - with error description
249 pub
250 fn make_a_aaaa_request(resolvers_opt: Option<Arc<ResolveConfig>>, req_name: impl Into<QDnsName>,
251 opts: QuerySetup) -> CDnsResult<Self>
252 {
253 let resolvers = resolvers_opt.unwrap_or(CACHE.clone_resolve_list()?);
254
255 // store the A and AAAA depending on order
256 let reqs: Vec<QDnsReq> =
257 match resolvers.family
258 {
259 ResolveConfigFamily::INET4_INET6 =>
260 {
261 let req_n: QDnsName = req_name.into();
262
263 vec![
264 QDnsReq::new(req_n.clone(), QType::A),
265 QDnsReq::new(req_n, QType::AAAA),
266 ]
267 },
268 ResolveConfigFamily::INET6_INET4 =>
269 {
270 let req_n: QDnsName = req_name.into();
271
272 vec![
273 QDnsReq::new(req_n.clone(), QType::AAAA),
274 QDnsReq::new(req_n, QType::A),
275 ]
276 },
277 ResolveConfigFamily::INET6 =>
278 {
279 vec![
280 QDnsReq::new(req_name.into(), QType::AAAA),
281 ]
282 },
283 ResolveConfigFamily::INET4 =>
284 {
285 vec![
286 QDnsReq::new(req_name.into(), QType::A),
287 ]
288 }
289 _ =>
290 {
291 // set default
292 let req_n: QDnsName = req_name.into();
293
294 vec![
295 QDnsReq::new(req_n.clone(), QType::A),
296 QDnsReq::new(req_n, QType::AAAA),
297 ]
298 }
299 };
300
301
302
303 return Ok(
304 Self
305 {
306 resolvers: resolvers,
307 ordered_req_list: reqs,
308 opts: opts,
309 }
310 );
311 }
312
313 /// Resolves the created query/ies.
314 ///
315 /// # Returns
316 ///
317 /// * [CDnsResult] with [QDnsQueryResult] which may contain results
318 ///
319 /// * [CDnsResult] with error
320 ///
321 /// Should not panic. MT-safe.
322 pub
323 fn query(mut self) -> QDnsQueryResult
324 {
325 // check if we need to measure time
326 let now =
327 if self.opts.measure_time == true
328 {
329 Some(Instant::now())
330 }
331 else
332 {
333 None
334 };
335
336 // determine where to look firstly i.e file -> bind, bind -> file
337 // or bind only, or file only
338 if self.resolvers.lookup.is_file_first()
339 {
340 let mut query_res =
341 match self.lookup_file(now.as_ref())
342 {
343 Ok(file) =>
344 {
345 if file.is_empty() == false
346 {
347 // remove records which was resolved
348 self.ordered_req_list.retain(|req|
349 {
350 return !file.contains_dnsreq(req);
351 }
352 );
353 }
354
355 file
356 },
357 Err(e) =>
358 {
359 write_error!(e);
360
361 QDnsQueryResult::default()
362 }
363 };
364
365
366 // if something left unresolved, try ask internet
367 if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_bind() == true
368 {
369 let res = self.process_request(now.as_ref());
370
371 query_res.extend(res);
372 }
373
374 return query_res;
375 }
376 else
377 {
378 let mut dns_res = self.process_request(now.as_ref());
379 if dns_res.is_empty() == false
380 {
381 // remove records which was resolved
382 self.ordered_req_list.retain(|req|
383 {
384 return !dns_res.contains_dnsreq(req);
385 }
386 );
387 }
388
389
390
391 if self.ordered_req_list.is_empty() == false && self.resolvers.lookup.is_file() == true
392 {
393 match self.lookup_file(now.as_ref())
394 {
395 Ok(res) =>
396 {
397 dns_res.extend(res);
398 },
399 Err(e) =>
400 {
401 write_error!(e);
402 }
403 }
404 }
405
406 return dns_res;
407 }
408 }
409
410 /// Returns timeout
411 fn get_timeout(&self) -> Duration
412 {
413 if let Some(timeout) = self.opts.timeout
414 {
415 return Duration::from_secs(timeout as u64);
416 }
417 else
418 {
419 return Duration::from_secs(self.resolvers.timeout as u64);
420 }
421 }
422
423 /// Searches in /etc/hosts
424 fn lookup_file(&mut self, now: Option<&Instant>) -> CDnsResult<QDnsQueryResult>
425 {
426 let mut dnsquries: QDnsQueryResult = QDnsQueryResult::default();
427
428 // check if the it is overriden
429 if self.opts.ign_hosts == false
430 {
431 let hlist = CACHE.clone_host_list()?;
432
433 for req in self.ordered_req_list.iter()
434 {
435 match *req.get_type()
436 {
437 QType::A | QType::AAAA =>
438 {
439 let req_name = String::from(req.get_req_name());
440
441 let Some(host_name_ent) = hlist.search_by_fqdn(req.get_type(), req_name.as_str())
442 else { continue };
443
444 let Some(drp) = DnsResponsePayload::new_local(*req.get_type(), host_name_ent)
445 else { continue };
446
447 // store to list
448 dnsquries.push(req.clone(), Ok(QDnsQuery::from_local(drp, now)));
449 },
450 QType::PTR =>
451 {
452 let Ok(ip) = IpAddr::try_from(req.get_req_name())
453 else { continue };
454
455 let Some(host_name_ent) = hlist.search_by_ip(&ip)
456 else { continue };
457
458 let Some(drp) = DnsResponsePayload::new_local(*req.get_type(), host_name_ent)
459 else { continue };
460
461 dnsquries.push(req.clone(), Ok(QDnsQuery::from_local(drp, now)));
462 },
463 _ =>
464 continue,
465 }
466 }
467
468
469 }
470
471 return Ok(dnsquries);
472 }
473
474 /// Creates socket based on config and flag. If `force_tcp` is set then Tcp tap will
475 /// be created.
476 #[inline]
477 fn create_socket(&self, force_tcp: bool, resolver: Arc<ResolveConfEntry>) -> CDnsResult<Box<dyn SocketTap>>
478 {
479 let is_tls = resolver.get_tls_type();
480
481 if is_tls == ConfigEntryTls::Tls
482 {
483 return NetworkTap::<TcpTlsConnection>::new(resolver, self.get_timeout());
484 }
485 else if is_tls == ConfigEntryTls::Https
486 {
487 return NetworkTap::<TcpHttpsConnection>::new(resolver, self.get_timeout());
488 }
489 else if self.resolvers.option_flags.is_force_tcp() == true || force_tcp == true
490 {
491 return NetworkTap::<TcpStream>::new(resolver, self.get_timeout());
492 }
493 else
494 {
495 return NetworkTap::<UdpSocket>::new(resolver, self.get_timeout());
496 }
497 }
498
499 /// Quering nameservers
500 fn process_request(&mut self, now: Option<&Instant>) -> QDnsQueryResult
501 {
502 let mut responses: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
503
504 if self.resolvers.option_flags.is_no_parallel() == true
505 {
506 for req in self.ordered_req_list.iter()
507 {
508 let mut last_resp: Option<CDnsResult<QDnsQuery>> = None;
509
510 for resolver in self.resolvers.get_resolvers_iter()
511 {
512 match self.query_exec_seq(now, resolver.clone(), req, None)
513 {
514 Ok(resp) =>
515 {
516 if resp.should_check_next_ns() == true
517 {
518 last_resp = Some(Ok(resp));
519
520 continue;
521 }
522 else
523 {
524 responses.push(req.clone(), Ok(resp));
525
526 let _ = last_resp.take();
527
528 break;
529 }
530 },
531 Err(e) =>
532 {
533 if last_resp.is_none() == true
534 {
535 last_resp = Some(Err(e));
536 }
537
538 continue;
539 }
540 }
541 } // for
542
543 responses.push(req.clone(), last_resp.take().unwrap());
544 }// for
545 }
546 else
547 {
548 // let mut responses: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
549
550 for resolver in self.resolvers.get_resolvers_iter()
551 {
552 if self.ordered_req_list.is_empty() == true
553 {
554 break;
555 }
556
557 match self.query_exec_pipelined(now, resolver.clone(), None)
558 {
559 Ok(resp) =>
560 {
561 for (qdns_res, qdns_que) in resp
562 {
563 if let Ok(ref resp) = qdns_que
564 {
565 if resp.should_check_next_ns() == false
566 {
567 self
568 .ordered_req_list
569 .retain(
570 |req_item|
571 req_item != &qdns_res
572 );
573 }
574 }
575
576 responses.push(qdns_res, qdns_que);
577 }
578 },
579 Err(e) =>
580 {
581 write_error!(e);
582
583 continue;
584 }
585 }
586 }
587 }
588
589 return responses;
590 }
591
592
593 fn query_exec_pipelined(
594 &self,
595 now: Option<&Instant>,
596 resolver: Arc<ResolveConfEntry>,
597 requery: Option<HashMap<DnsRequestHeader, QDnsReq>>,
598 ) -> CDnsResult<QDnsQueryResult>
599 {
600 let force_tcp = self.resolvers.option_flags.is_force_tcp() || requery.is_some();
601
602 let mut query_headers: HashMap<DnsRequestHeader, QDnsReq> =
603 if let Some(requer) = requery
604 {
605 let pkts_ids = requer.iter().map(|q| q.0.get_id()).collect::<BTreeSet<u16>>();
606
607 // regenerate que ID and collect
608 requer
609 .into_iter()
610 .map(
611 |(mut qrr, qdr)|
612 {
613 loop
614 {
615 qrr.regenerate_id();
616
617 if pkts_ids.contains(&qrr.get_id()) == false
618 {
619 break;
620 }
621 }
622 (qrr, qdr)
623 })
624 .collect::<HashMap<DnsRequestHeader, QDnsReq>>()
625 }
626 else
627 {
628 let mut pkts_ids: BTreeSet<u16> = BTreeSet::new();
629
630 // create que
631 self
632 .ordered_req_list
633 .iter()
634 .map(
635 |query|
636 {
637
638 let mut drh_res = DnsRequestHeader::try_from(query);
639
640 loop
641 {
642 if let Ok(ref mut drh) = drh_res
643 {
644 if pkts_ids.contains(&drh.get_id()) == true
645 {
646 drh.regenerate_id();
647
648 continue;
649 }
650 else
651 {
652 pkts_ids.insert(drh.get_id());
653 break;
654 }
655 }
656 else
657 {
658 break;
659 }
660 }
661
662 drh_res.map(|dh| (dh, query.clone()))
663 }
664 )
665 .collect::<CDnsResult<HashMap<DnsRequestHeader, QDnsReq>>>()?
666 };
667
668 // create socket and store
669 let mut tap = self.create_socket(force_tcp, resolver.clone())?;
670
671 tap.connect(true, CDdnsGlobals::get_tcp_conn_timeout())?;
672
673 // send everything
674 for qh in query_headers.iter()
675 {
676 let pkt = qh.0.to_bytes(tap.should_append_len())?;
677
678 tap.send(pkt.as_slice())?;
679 }
680
681 let mut resp: QDnsQueryResult = QDnsQueryResult::with_capacity(self.ordered_req_list.len());
682 let mut requery: HashMap<DnsRequestHeader, QDnsReq> = HashMap::new();
683 // poll socket
684
685 loop
686 {
687 if query_headers.len() == 0
688 {
689 break;
690 }
691
692 /*let mut poll_tap = [PollFd::new(tap.get_fd().unwrap(), PollFlags::from(PollFlags::POLLIN))];
693
694 let poll_res =
695 nix::poll::poll(&mut poll_tap, self.resolvers.timeout * 1000 as u16)
696 .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
697
698 if poll_res == 0
699 {
700 internal_error!(CDnsErrorType::IoError, "Timeout");
701 }
702
703 let Some(revents) = poll_tap[0].revents()
704 else
705 {
706 internal_error!(CDnsErrorType::IoError, "Crate NIX returned None due to unknown data returned by kernel!\
707 Try update nix crate! Probably it is outdated!");
708 };
709
710 if revents.intersects(PollFlags::POLLERR | PollFlags::POLLHUP | PollFlags::POLLNVAL ) == true
711 {
712 write_error!(internal_error_map!(CDnsErrorType::IoError, "socket poll error {:?}", revents));
713 break;
714 }
715 else if revents.intersects(PollFlags::POLLIN) == true
716 {*/
717 if tap.poll_read()? == false
718 {
719 // timeout
720 break;
721 }
722
723 let ans = tap.recv()?;//self.read_response(tap.as_mut())?;
724
725 let Some((query_header, qdnsreq)) = query_headers.remove_entry(&ans.req_header)
726 else
727 {
728 internal_error!(CDnsErrorType::IoError, "can not find response with request: {}", ans.req_header);
729 };
730
731 ans.verify(&query_header)?;
732
733 // verified
734 let qdns_resp = QDnsQuery::from_response(tap.get_remote_addr(), ans, now);
735
736 if let Ok(ref qdns) = qdns_resp
737 {
738 if qdns.get_status().should_try_tcp() == true && force_tcp == false
739 {
740 requery.insert(query_header, qdnsreq);
741 }
742 else
743 {
744 resp.push(qdnsreq, qdns_resp);
745 }
746 }
747 else
748 {
749 resp.push(qdnsreq, qdns_resp);
750 }
751 // }
752 }
753
754 if requery.is_empty() == false
755 {
756 let res = self.query_exec_pipelined(now, resolver, Some(requery))?;
757
758 resp.extend(res);
759 }
760
761 return Ok(resp);
762 }
763
764 /// Runs sequentually
765 fn query_exec_seq(
766 &self,
767 now: Option<&Instant>,
768 resolver: Arc<ResolveConfEntry>,
769 query: &QDnsReq,
770 requery: Option<DnsRequestHeader>,
771 ) -> CDnsResult<QDnsQuery>
772 {
773 let force_tcp = self.resolvers.option_flags.is_force_tcp() || requery.is_some();
774
775 // create socket and store
776 let mut tap = self.create_socket(force_tcp, resolver.clone())?;
777
778 let query_header =
779 // form the list of reqests binded to taps
780 if let Some(mut requery) = requery
781 {
782 requery.regenerate_id();
783
784 requery
785 }
786 else
787 {
788 let drh_req = DnsRequestHeader::try_from(query)?;
789
790 drh_req
791 };
792
793 let res =
794 {
795 // open connection, with timeout because working in non parallel mode
796 tap.connect(false, CDdnsGlobals::get_tcp_conn_timeout())?;
797
798 // convert to byte packet (the error returned is global error i.e OOM)
799 let pkt = query_header.to_bytes(tap.should_append_len())?;
800
801 // send to DNS server, if error happens, then stop processing this server
802 tap.send(pkt.as_slice())?;
803
804 let ans = tap.recv()?; //self.read_response(tap.as_mut())?;
805
806 ans.verify(&query_header)?;
807
808 // verified
809 let resp = QDnsQuery::from_response(tap.get_remote_addr(), ans, now)?;
810
811 Ok(resp)
812 };
813
814 if (res.is_ok() == true && res.as_ref().unwrap().status.should_try_tcp() == false) ||
815 (res.is_err() == true && force_tcp == true)
816 {
817 return res;
818 }
819
820
821 return
822 self.query_exec_seq(now, resolver.clone(), query, Some(query_header));
823 }
824
825 /*
826 // Reads response from tap
827 fn read_response(&self, socktap: &mut (dyn SocketTap)) -> CDnsResult<DnsRequestAnswer>
828 {
829 if socktap.should_append_len() == false && socktap.is_encrypted() == false
830 {
831 let mut rcvbuf = vec![0_u8; 1457];
832
833 // receive message
834 let n = socktap.recv(rcvbuf.as_mut_slice())?;
835
836 // parsing response to structure
837 return DnsRequestAnswer::parse(&rcvbuf, n); //DnsRequestAnswer::try_from(rcvbuf.as_slice(), n, socktap.is_tcp())?;
838 }
839 else if socktap.should_append_len() == false && socktap.is_encrypted() == true
840 {
841 let mut rcvbuf = vec![0_u8; 1457];
842 }
843 else
844 {
845 let mut pkg_pen: [u8; 2] = [0, 0];
846 let n = socktap.recv(&mut pkg_pen)?;
847
848 if n == 0
849 {
850 internal_error!(CDnsErrorType::IoError, "tcp received zero len message!");
851 }
852 else if n != 2
853 {
854 internal_error!(CDnsErrorType::IoError, "tcp expected 2 bytes to be read!");
855 }
856
857 let ln = u16::from_be_bytes(pkg_pen);
858
859 let mut rcvbuf = vec![0_u8; ln as usize];
860
861 // receive message
862 let mut n = socktap.recv(rcvbuf.as_mut_slice())?;
863
864 if n == 0
865 {
866 internal_error!(CDnsErrorType::IoError, "tcp received zero len message!");
867 }
868 else if n == 1
869 {
870 n = socktap.recv(&mut rcvbuf[1..])?;
871
872 if n == 0
873 {
874 internal_error!(CDnsErrorType::IoError, "tcp received zero len message again!");
875 }
876
877 n += 1;
878 }
879
880 return DnsRequestAnswer::parse(&rcvbuf, n);
881 }
882 }*/
883}
884
885#[cfg(test)]
886
887mod tests
888{
889 use std::net::IpAddr;
890
891 use crate::{common::{byte2hexchar, ip2pkt, RecordPTR, RecordReader}, sync::{query::QDns, QDnsName}, QDnsQueryRec, QType, QuerySetup};
892
893 #[test]
894 fn test_ip2pkt()
895 {
896 use std::time::Instant;
897 use std::net::{IpAddr, Ipv4Addr};
898
899 let test = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
900
901 let now = Instant::now();
902
903 let res = ip2pkt(&test);
904
905 let elapsed = now.elapsed();
906 println!("Elapsed: {:.2?}", elapsed);
907
908 assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
909
910 let res = res.unwrap();
911 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";
912
913 assert_eq!(res.as_slice(), ctrl);
914 }
915
916
917 #[test]
918 fn test_byte2hexchar()
919 {
920 assert_eq!(byte2hexchar(1), 0x31);
921 assert_eq!(byte2hexchar(9), 0x39);
922 assert_eq!(byte2hexchar(10), 'a' as u8);
923 assert_eq!(byte2hexchar(15), 'f' as u8);
924 }
925
926 #[test]
927 fn reverse_lookup_test()
928 {
929 use std::time::Instant;
930
931 let ipp: IpAddr = "8.8.8.8".parse().unwrap();
932 //let test = QDnsName::try_from(&ipp).unwrap();
933
934 let mut query_setup = QuerySetup::default();
935 query_setup.set_measure_time(true);
936
937 let now = Instant::now();
938
939 let mut dns_req =
940 QDns::make_empty(None, query_setup).unwrap();
941
942 dns_req.add_request(QType::PTR, ipp);
943 let res = dns_req.query();
944
945 let elapsed = now.elapsed();
946 println!("Elapsed: {:.2?}", elapsed);
947
948 println!("{}", res);
949
950 assert_eq!(res.is_empty(), false);
951
952 let recs = res.collect_ok();
953 let rec = &recs[0];
954 //assert_eq!(rec.server.as_str(), "/etc/hosts");
955 assert_eq!(rec.status, QDnsQueryRec::Ok);
956
957 assert_eq!(rec.resp.len(), 1);
958 assert_eq!(rec.resp[0].rdata, RecordPTR::wrap(RecordPTR{ fqdn: "dns.google".to_string() }));
959 }
960
961 #[test]
962 fn reverse_lookup_hosts_test()
963 {
964 use std::time::Instant;
965
966 let ipp: IpAddr = "127.0.0.1".parse().unwrap();
967 //let test = QDnsName::try_from(&ipp).unwrap();
968
969 let now = Instant::now();
970
971 let mut query_setup = QuerySetup::default();
972 query_setup.set_measure_time(true);
973
974 let mut dns_req =
975 QDns::make_empty(None, query_setup).unwrap();
976
977 dns_req.add_request(QType::PTR, ipp);
978
979 let res = dns_req.query();
980
981 let elapsed = now.elapsed();
982 println!("Elapsed: {:.2?}", elapsed);
983
984 println!("{}", res);
985
986 assert_eq!(res.is_empty(), false);
987
988 let recs = res.collect_ok();
989 let rec = &recs[0];
990
991 assert_eq!(rec.server.as_str(), "/etc/hosts");
992 assert_eq!(rec.status, QDnsQueryRec::Ok);
993
994 assert_eq!(rec.resp.len(), 1);
995 assert_eq!(rec.resp[0].rdata, RecordPTR::wrap(RecordPTR{ fqdn: "localhost".to_string() }));
996 }
997
998
999 #[test]
1000 fn reverse_lookup_a()
1001 {
1002 use std::time::Instant;
1003
1004 // let test = QDnsName::try_from("dns.google").unwrap();
1005
1006 let mut query_setup = QuerySetup::default();
1007 query_setup.set_measure_time(true);
1008
1009
1010 let res =
1011 QDns::make_a_aaaa_request(None, "dns.google", query_setup).unwrap();
1012
1013
1014 let now = Instant::now();
1015 let res = res.query();
1016
1017
1018 let elapsed = now.elapsed();
1019 println!("Elapsed: {:.2?}", elapsed);
1020
1021 /* assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1022
1023 let res = res.unwrap();*/
1024 println!("{}", res);
1025 }
1026
1027
1028 #[test]
1029 fn truncation_test_1()
1030 {
1031 use std::time::Instant;
1032
1033 //let test = QDnsName::try_from("truncate-zyxw11.go.dnscheck.tools").unwrap();
1034
1035 let mut query_setup = QuerySetup::default();
1036 query_setup.set_measure_time(true);
1037
1038
1039 let mut res =
1040 QDns::make_empty(None, query_setup).unwrap();
1041
1042 res.add_request(QType::TXT, "truncate-zyxw11.go.dnscheck.tools");
1043
1044
1045 let now = Instant::now();
1046 let res = res.query();
1047
1048
1049 let elapsed = now.elapsed();
1050 println!("Elapsed: {:.2?}", elapsed);
1051
1052 /* assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1053
1054 let res = res.unwrap();*/
1055 println!("{}", res);
1056 }
1057
1058 #[test]
1059 fn truncation_test_2()
1060 {
1061 use std::time::Instant;
1062
1063 // let test = QDnsName::try_from("dnscheck.tools").unwrap();
1064 // let test2 = QDnsName::try_from("localhost").unwrap();
1065 // let test3 = QDnsName::try_from("unknownnonexistentdomain.com").unwrap();
1066 // let test4 = QDnsName::try_from("example.com").unwrap();
1067
1068 let mut query_setup = QuerySetup::default();
1069 query_setup.set_measure_time(true);
1070
1071
1072 let mut res =
1073 QDns::make_empty(None, query_setup).unwrap();
1074
1075 res.add_request(QType::A, "dnscheck.tools");
1076 res.add_request(QType::A, "localhost");
1077 res.add_request(QType::A, "unknownnonexistentdomain.com");
1078 res.add_request(QType::TXT, "example.com");
1079
1080
1081 let now = Instant::now();
1082 let res = res.query();
1083
1084
1085 let elapsed = now.elapsed();
1086 println!("Elapsed: {:.2?}", elapsed);
1087
1088
1089 let (ans, errs) = res.collect_split();
1090
1091 println!("ANSWERS");
1092 for a in ans
1093 {
1094 println!("-----\n{} \n{}", a.0, a.1.unwrap());
1095 }
1096
1097 println!("ERRORS");
1098 for b in errs
1099 {
1100 println!("-----\n{} \n{}", b.0, b.1.err().unwrap());
1101 }
1102 }
1103}