1use std::{collections::HashMap, iter, sync::Arc};
12
13use tracing::{debug, error, info, trace, warn};
14
15#[cfg(feature = "metrics")]
16use crate::metrics::CatalogMetrics;
17#[cfg(feature = "__dnssec")]
18use crate::{
19 dnssec::NxProofKind,
20 proto::{
21 dnssec::{DnssecSummary, rdata::DNSSECRData},
22 rr::RData,
23 serialize::binary::BinEncoder,
24 },
25 zone_handler::Nsec3QueryInfo,
26};
27use crate::{
28 net::runtime::Time,
29 proto::{
30 op::{Edns, LowerQuery, Message, MessageType, Metadata, OpCode, ResponseCode},
31 rr::{
32 LowerName, RecordSet, RecordType,
33 rdata::opt::{EdnsCode, EdnsOption, NSIDPayload},
34 },
35 },
36 server::{Request, RequestHandler, RequestInfo, ResponseHandler, ResponseInfo},
37 zone_handler::{
38 AuthLookup, LookupControlFlow, LookupError, LookupOptions, LookupRecords,
39 MessageResponseBuilder, ZoneHandler, ZoneType,
40 },
41};
42#[cfg(all(feature = "__dnssec", feature = "recursor"))]
43use crate::{
44 net::{DnsError, NetError},
45 resolver::recursor,
46};
47
48#[derive(Default)]
50pub struct Catalog {
51 nsid_payload: Option<NSIDPayload>,
52 handlers: HashMap<LowerName, Vec<Arc<dyn ZoneHandler>>>,
53 #[cfg(feature = "metrics")]
54 metrics: CatalogMetrics,
55}
56
57#[async_trait::async_trait]
58impl RequestHandler for Catalog {
59 async fn handle_request<R: ResponseHandler, T: Time>(
66 &self,
67 request: &Request,
68 response_handle: R,
69 ) -> ResponseInfo {
70 trace!("request: {:?}", request);
71
72 let mut resp_edns: Edns;
73
74 let response_edns = if let Some(req_edns) = request.edns.as_ref() {
76 resp_edns = Edns::new();
77
78 let our_version = 0;
81 resp_edns.set_dnssec_ok(true);
82 resp_edns.set_max_payload(req_edns.max_payload().max(512));
83 resp_edns.set_version(our_version);
84
85 if req_edns.version() > our_version {
86 warn!(
87 "request edns version greater than {}: {}",
88 our_version,
89 req_edns.version()
90 );
91 return send_error_response(
92 request,
93 ResponseCode::BADVERS,
94 Some(&resp_edns),
95 response_handle,
96 )
97 .await;
98 }
99
100 match (req_edns.option(EdnsCode::NSID), &self.nsid_payload) {
102 (Some(request_option), Some(payload)) => {
105 if !request_option.is_empty() {
108 warn!("ignoring non-empty EDNS NSID request payload")
109 }
110 resp_edns
111 .options_mut()
112 .insert(EdnsOption::NSID(payload.clone()));
113 }
114 (Some(_), None) => {
116 trace!("ignoring EDNS NSID request - no response payload configured")
117 }
118 (None, _) => {}
121 };
122
123 Some(&resp_edns)
124 } else {
125 None
126 };
127
128 let now = T::current_time();
129 match request.metadata.message_type {
130 MessageType::Query => match request.metadata.op_code {
133 OpCode::Query => {
134 debug!("query received: {}", request.metadata.id);
135 self.lookup(request, response_edns, now, response_handle)
136 .await
137 }
138 OpCode::Update => {
139 debug!("update received: {}", request.metadata.id);
140 self.update(request, response_edns, now, response_handle)
141 .await
142 }
143 c => {
144 warn!("unimplemented op_code: {:?}", c);
145 send_error_response(
146 request,
147 ResponseCode::NotImp,
148 response_edns,
149 response_handle,
150 )
151 .await
152 }
153 },
154 MessageType::Response => {
155 warn!(
156 "got a response as a request from id: {}",
157 request.metadata.id
158 );
159 send_error_response(
160 request,
161 ResponseCode::FormErr,
162 response_edns,
163 response_handle,
164 )
165 .await
166 }
167 }
168 }
169}
170
171impl Catalog {
172 pub fn new() -> Self {
174 Self {
175 handlers: HashMap::new(),
176 nsid_payload: None,
177 #[cfg(feature = "metrics")]
178 metrics: CatalogMetrics::default(),
179 }
180 }
181
182 pub fn upsert(&mut self, name: LowerName, handlers: Vec<Arc<dyn ZoneHandler>>) {
189 #[cfg(feature = "metrics")]
190 for handler in handlers.iter() {
191 self.metrics.add_handler(handler.as_ref())
192 }
193
194 self.handlers.insert(name, handlers);
195 }
196
197 pub fn remove(&mut self, name: &LowerName) -> Option<Vec<Arc<dyn ZoneHandler>>> {
199 self.handlers.remove(name)
202 }
203
204 pub fn set_nsid(&mut self, payload: Option<NSIDPayload>) {
211 self.nsid_payload = payload
212 }
213
214 pub fn nsid(&self) -> Option<&NSIDPayload> {
218 self.nsid_payload.as_ref()
219 }
220
221 pub async fn update<R: ResponseHandler>(
272 &self,
273 update: &Request,
274 response_edns: Option<&Edns>,
275 now: u64,
276 mut response_handle: R,
277 ) -> ResponseInfo {
278 let Ok(request_info) = update.request_info() else {
286 warn!("invalid update request, zone count must be one");
287 return send_error_response(
288 update,
289 ResponseCode::FormErr,
290 response_edns,
291 response_handle,
292 )
293 .await;
294 };
295 let ztype = request_info.query.query_type();
296
297 if ztype != RecordType::SOA {
298 warn!("invalid update request zone type must be SOA, ztype: {ztype}");
299 return send_error_response(
300 update,
301 ResponseCode::FormErr,
302 response_edns,
303 response_handle,
304 )
305 .await;
306 }
307
308 if let Some(handlers) = self.find(request_info.query.name()) {
310 #[allow(clippy::never_loop)]
311 for handler in handlers {
312 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
313 let (response_code, signer) = match handler.zone_type() {
314 ZoneType::Secondary => {
315 error!("secondary forwarding for update not yet implemented");
316 (ResponseCode::NotImp, None)
317 }
318 ZoneType::Primary => {
319 let (update_result, signer) = handler.update(update, now).await;
320 match update_result {
321 Ok(_) => (ResponseCode::NoError, signer),
323 Err(response_code) => (response_code, signer),
324 }
325 }
326 _ => (ResponseCode::NotAuth, None),
327 };
328
329 let response = MessageResponseBuilder::new(&update.queries, response_edns);
330 let mut response_meta =
331 Metadata::new(update.metadata.id, MessageType::Response, OpCode::Update);
332 response_meta.response_code = response_code;
333 #[cfg_attr(not(feature = "__dnssec"), expect(unused_mut))]
334 let mut response = response.build_no_records(response_meta);
335
336 #[cfg(feature = "__dnssec")]
337 if let Some(signer) = signer {
338 let mut tbs_response_buf = Vec::with_capacity(512);
339 let mut encoder = BinEncoder::new(&mut tbs_response_buf);
340 let mut response_meta =
341 Metadata::new(update.metadata.id, MessageType::Response, OpCode::Update);
342 response_meta.response_code = response_code;
343 let tbs_response = MessageResponseBuilder::new(&update.queries, response_edns)
344 .build_no_records(response_meta);
345 if let Err(error) = tbs_response.destructive_emit(&mut encoder) {
346 error!(%error, "error encoding response");
347 return send_error_response(
348 update,
349 ResponseCode::ServFail,
350 response_edns,
351 response_handle,
352 )
353 .await;
354 }
355 match signer.sign(&tbs_response_buf) {
356 Ok(signature) => response.set_signature(signature),
357 Err(error) => {
358 error!(%error, "error signing response");
359 return send_error_response(
360 update,
361 ResponseCode::ServFail,
362 response_edns,
363 response_handle,
364 )
365 .await;
366 }
367 }
368 }
369
370 match response_handle.send_response(response).await {
371 Err(error) => {
372 error!(%error, "error sending message");
373 return ResponseInfo::serve_failed(update);
374 }
375 Ok(response_info) => return response_info,
376 }
377 }
378 };
379
380 send_error_response(
381 update,
382 ResponseCode::ServFail,
383 response_edns,
384 response_handle,
385 )
386 .await
387 }
388
389 pub fn contains(&self, name: &LowerName) -> bool {
399 self.handlers.contains_key(name)
400 }
401
402 pub async fn lookup<R: ResponseHandler>(
410 &self,
411 request: &Request,
412 response_edns: Option<&Edns>,
413 now: u64,
414 response_handle: R,
415 ) -> ResponseInfo {
416 let Ok(request_info) = request.request_info() else {
417 return send_error_response(
419 request,
420 ResponseCode::FormErr,
421 response_edns,
422 response_handle,
423 )
424 .await;
425 };
426 let handlers = self.find(request_info.query.name());
427
428 let Some(handlers) = handlers else {
429 return send_error_response(
431 request,
432 ResponseCode::Refused,
433 response_edns,
434 response_handle,
435 )
436 .await;
437 };
438
439 if request_info.query.query_type() == RecordType::AXFR {
440 zone_transfer(
441 request_info,
442 handlers,
443 request,
444 response_edns,
445 now,
446 response_handle.clone(),
447 )
448 .await
449 } else {
450 lookup(
451 request_info,
452 handlers,
453 request,
454 response_edns,
455 response_handle.clone(),
456 #[cfg(feature = "metrics")]
457 &self.metrics,
458 )
459 .await
460 }
461 }
462
463 pub fn find(&self, name: &LowerName) -> Option<&Vec<Arc<dyn ZoneHandler + 'static>>> {
465 debug!("searching zone handlers for: {name}");
466 self.handlers.get(name).or_else(|| {
467 if !name.is_root() {
468 let name = name.base_name();
469 self.find(&name)
470 } else {
471 None
472 }
473 })
474 }
475}
476
477async fn lookup<R: ResponseHandler + Unpin>(
478 request_info: RequestInfo<'_>,
479 handlers: &[Arc<dyn ZoneHandler>],
480 request: &Request,
481 response_edns: Option<&Edns>,
482 mut response_handle: R,
483 #[cfg(feature = "metrics")] metrics: &CatalogMetrics,
484) -> ResponseInfo {
485 let edns = request.edns.as_ref();
486 let lookup_options = LookupOptions::from_edns(edns);
487 let request_id = request.metadata.id;
488
489 if lookup_options.dnssec_ok {
490 info!("request: {request_id} lookup_options: {lookup_options:?}");
491 }
492
493 let query = request_info.query;
494
495 for (index, handler) in handlers.iter().enumerate() {
496 debug!(
497 "performing {query} on zone handler {origin} with request id {request_id}",
498 origin = handler.origin(),
499 );
500
501 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
504 let (mut result, mut signer) = handler.search(request, lookup_options).await;
505 #[cfg(feature = "metrics")]
506 metrics.update_zone_lookup(handler.as_ref(), &result);
507
508 if let LookupControlFlow::Skip = result {
509 trace!("catalog::lookup: zone handler did not handle request");
510 continue;
511 } else if result.is_continue() {
512 trace!("catalog::lookup: zone handler did handle request with continue");
513
514 for (continue_index, consult_handler) in handlers.iter().enumerate() {
517 if continue_index == index {
518 trace!("skipping current zone handler consult (index {continue_index})");
519 continue;
520 } else {
521 trace!("calling zone handler consult (index {continue_index})");
522 }
523
524 let (new_result, new_signer) = consult_handler
525 .consult(
526 request_info.query.name(),
527 request_info.query.query_type(),
528 Some(&request_info),
529 LookupOptions::from_edns(response_edns),
530 result,
531 )
532 .await;
533 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
534 if let Some(new_signer) = new_signer {
535 signer = Some(new_signer);
536 }
537 result = new_result;
538 }
539 } else {
540 trace!("catalog::lookup: zone handler did handle request with break");
541 }
542
543 let Some(result) = result.map_result() else {
546 error!("impossible skip detected after final lookup result");
547 return send_error_response(
548 request,
549 ResponseCode::ServFail,
550 response_edns,
551 response_handle,
552 )
553 .await;
554 };
555
556 let response_message = build_response(
557 result,
558 &**handler,
559 request_id,
560 &request.metadata,
561 query,
562 edns,
563 )
564 .await;
565
566 #[cfg_attr(not(feature = "__dnssec"), expect(unused_mut))]
567 let mut message_response = MessageResponseBuilder::new(&request.queries, response_edns)
568 .build(
569 response_message.metadata,
570 response_message.answers.iter(),
571 response_message.authorities.iter(),
572 iter::empty(),
573 response_message.additionals.iter(),
574 );
575
576 #[cfg(feature = "__dnssec")]
577 if let Some(signer) = signer {
578 let mut tbs_response_buf = Vec::with_capacity(512);
579 let mut encoder = BinEncoder::new(&mut tbs_response_buf);
580 let tbs_response = MessageResponseBuilder::new(&request.queries, response_edns).build(
581 response_message.metadata,
582 response_message.answers.iter(),
583 response_message.authorities.iter(),
584 iter::empty(),
585 response_message.additionals.iter(),
586 );
587 if let Err(error) = tbs_response.destructive_emit(&mut encoder) {
588 error!(%error, "error encoding response");
589 return send_error_response(
590 request,
591 ResponseCode::ServFail,
592 response_edns,
593 response_handle,
594 )
595 .await;
596 }
597 match signer.sign(&tbs_response_buf) {
598 Ok(signature) => message_response.set_signature(signature),
599 Err(error) => {
600 error!(%error, "error signing response");
601 return send_error_response(
602 request,
603 ResponseCode::ServFail,
604 response_edns,
605 response_handle,
606 )
607 .await;
608 }
609 }
610 }
611
612 #[cfg(feature = "metrics")]
613 metrics.update_request_response(query, response_message.answers.iter());
614
615 match response_handle.send_response(message_response).await {
616 Err(error) => {
617 error!(%error, "error sending response");
618 return ResponseInfo::serve_failed(request);
619 }
620 Ok(response_info) => return response_info,
621 }
622 }
623
624 error!("end of chained zone handler loop reached with all zone handlers not answering");
625 send_error_response(
626 request,
627 ResponseCode::ServFail,
628 response_edns,
629 response_handle,
630 )
631 .await
632}
633
634async fn zone_transfer(
635 request_info: RequestInfo<'_>,
636 handlers: &[Arc<dyn ZoneHandler>],
637 request: &Request,
638 response_edns: Option<&Edns>,
639 now: u64,
640 mut response_handle: impl ResponseHandler,
641) -> ResponseInfo {
642 let request_edns = request.edns.as_ref();
643 let lookup_options = LookupOptions::from_edns(request_edns);
644 for handler in handlers.iter() {
645 debug!(
646 query = %request_info.query,
647 origin = %handler.origin(),
648 request_id = request.metadata.id,
649 "performing zone transfer"
650 );
651 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
652 let Some((result, signer)) = handler.zone_transfer(request, lookup_options, now).await
653 else {
654 continue;
655 };
656
657 let mut response_meta = Metadata::response_from_request(&request.metadata);
658 let zone_transfer = match result {
659 Ok(zone_transfer) => {
660 response_meta.response_code = ResponseCode::NoError;
661 response_meta.authoritative = true;
662 Some(zone_transfer)
663 }
664 Err(e) => {
665 match e {
666 LookupError::ResponseCode(
667 rcode @ ResponseCode::Refused | rcode @ ResponseCode::NotAuth,
668 ) => {
669 response_meta.response_code = rcode;
670 }
671 _ => {
672 if e.is_nx_domain() {
673 response_meta.response_code = ResponseCode::NXDomain;
674 }
675 }
676 }
677 None
678 }
679 };
680
681 #[cfg_attr(not(feature = "__dnssec"), expect(unused_mut))]
683 let mut message_response = MessageResponseBuilder::new(&request.queries, response_edns)
684 .build(
685 response_meta,
686 zone_transfer
687 .iter()
688 .flat_map(|zone_transfer| zone_transfer.iter()),
689 iter::empty(),
690 iter::empty(),
691 iter::empty(),
692 );
693
694 #[cfg(feature = "__dnssec")]
695 if let Some(signer) = signer {
696 let mut tbs_response_buf = Vec::with_capacity(512);
697 let mut encoder = BinEncoder::new(&mut tbs_response_buf);
698 let tbs_response = MessageResponseBuilder::new(&request.queries, response_edns).build(
699 response_meta,
700 zone_transfer
701 .iter()
702 .flat_map(|zone_transfer| zone_transfer.iter()),
703 iter::empty(),
704 iter::empty(),
705 iter::empty(),
706 );
707 if let Err(error) = tbs_response.destructive_emit(&mut encoder) {
708 error!(%error, "error encoding response");
709 return send_error_response(
710 request,
711 ResponseCode::ServFail,
712 response_edns,
713 response_handle,
714 )
715 .await;
716 }
717 match signer.sign(&tbs_response_buf) {
718 Ok(signature) => message_response.set_signature(signature),
719 Err(error) => {
720 error!(%error, "error signing response");
721 return send_error_response(
722 request,
723 ResponseCode::ServFail,
724 response_edns,
725 response_handle,
726 )
727 .await;
728 }
729 }
730 }
731
732 match response_handle.send_response(message_response).await {
733 Err(error) => {
734 error!(%error, "error sending response");
735 return ResponseInfo::serve_failed(request);
736 }
737 Ok(response_info) => return response_info,
738 }
739 }
740
741 error!("end of chained zone handler loop with all zone handlers not answering");
742 send_error_response(
743 request,
744 ResponseCode::ServFail,
745 response_edns,
746 response_handle,
747 )
748 .await
749}
750
751async fn send_error_response(
754 request: &Request,
755 response_code: ResponseCode,
756 mut response_edns: Option<&Edns>,
757 mut response_handle: impl ResponseHandler,
758) -> ResponseInfo {
759 let mut new_edns: Edns;
760 if response_code.high() != 0 {
761 if let Some(edns) = response_edns {
762 new_edns = edns.clone();
763 new_edns.set_rcode_high(response_code.high());
764 response_edns = Some(&new_edns);
765 }
766 }
767 let response = MessageResponseBuilder::new(&request.queries, response_edns)
768 .error_msg(&request.metadata, response_code);
769 match response_handle.send_response(response).await {
770 Ok(r) => r,
771 Err(error) => {
772 error!(%error, "failed to send response");
773 ResponseInfo::serve_failed(request)
774 }
775 }
776}
777
778async fn build_response(
780 result: Result<AuthLookup, LookupError>,
781 handler: &dyn ZoneHandler,
782 request_id: u16,
783 request_meta: &Metadata,
784 query: &LowerQuery,
785 edns: Option<&Edns>,
786) -> Message {
787 let lookup_options = LookupOptions::from_edns(edns);
788
789 match handler.zone_type() {
790 ZoneType::Primary | ZoneType::Secondary => {
791 build_authoritative_response(
792 result,
793 handler,
794 request_meta,
795 lookup_options,
796 request_id,
797 query,
798 )
799 .await
800 }
801 ZoneType::External => {
802 build_forwarded_response(
803 result,
804 request_meta,
805 #[cfg(feature = "__dnssec")]
806 handler.can_validate_dnssec(),
807 query,
808 lookup_options,
809 )
810 .await
811 }
812 }
813}
814
815async fn build_authoritative_response(
817 response: Result<AuthLookup, LookupError>,
818 handler: &dyn ZoneHandler,
819 request_meta: &Metadata,
820 lookup_options: LookupOptions,
821 _request_id: u16,
822 query: &LowerQuery,
823) -> Message {
824 let mut response_meta = Metadata::response_from_request(request_meta);
825 response_meta.authoritative = true;
826
827 let mut message = Message::new(
828 response_meta.id,
829 response_meta.message_type,
830 response_meta.op_code,
831 );
832 message.add_query(query.original().clone());
833
834 let answers = match response {
839 Ok(records) => {
840 response_meta.response_code = ResponseCode::NoError;
841 Some(records)
842 }
843 Err(LookupError::ResponseCode(
845 rcode @ ResponseCode::Refused | rcode @ ResponseCode::NotAuth,
846 )) => {
847 response_meta.response_code = rcode;
848 message.metadata = response_meta;
849 return message;
850 }
851 Err(e) => {
852 response_meta.response_code = if e.is_nx_domain() {
853 ResponseCode::NXDomain
854 } else {
855 ResponseCode::NoError
856 };
857 None
858 }
859 };
860
861 #[cfg_attr(not(feature = "__dnssec"), allow(unused_variables))]
862 let (ns, soa) = if let Some(answers) = &answers {
863 if query.query_type().is_soa() {
865 let future = handler.lookup(handler.origin(), RecordType::NS, None, lookup_options);
869 match future.await.map_result() {
870 Some(Ok(ns)) => (Some(ns), None),
871 Some(Err(error)) => {
872 warn!(%error, "ns_lookup errored");
873 (None, None)
874 }
875 None => {
876 warn!("ns_lookup unexpected skip");
877 (None, None)
878 }
879 }
880 } else {
881 #[cfg(feature = "__dnssec")]
882 {
883 let has_wildcard_match = answers.iter().any(|rr| match &rr.data {
884 RData::DNSSEC(DNSSECRData::RRSIG(rrsig)) => {
885 rrsig.input().num_labels < rr.name.num_labels()
886 }
887 _ => false,
888 });
889
890 let res = match handler.nx_proof_kind() {
891 Some(NxProofKind::Nsec3 {
892 algorithm,
893 salt,
894 iterations,
895 opt_out: _,
896 }) => handler
897 .nsec3_records(
898 Nsec3QueryInfo {
899 qname: query.name(),
900 qtype: query.query_type(),
901 has_wildcard_match,
902 algorithm: *algorithm,
903 salt,
904 iterations: *iterations,
905 },
906 lookup_options,
907 )
908 .await
909 .map_result(),
910 Some(NxProofKind::Nsec) if has_wildcard_match => handler
911 .nsec_records(query.name(), lookup_options)
912 .await
913 .map_result(),
914 _ => None,
915 };
916
917 match res {
918 Some(Ok(nsecs)) => (Some(nsecs), None),
920 Some(Err(error)) => {
921 warn!(%error, request_id = _request_id, "failed to lookup nsecs for request");
922 (None, None)
923 }
924 None => {
925 warn!(
926 request_id = _request_id,
927 "unexpected lookup skip for request"
928 );
929 (None, None)
930 }
931 }
932 }
933 #[cfg(not(feature = "__dnssec"))]
934 (None, None)
935 }
936 } else {
937 let nsecs = if lookup_options.dnssec_ok {
938 #[cfg(feature = "__dnssec")]
939 {
940 debug!("request: {_request_id} non-existent adding nsecs");
942 match handler.nx_proof_kind() {
943 Some(nx_proof_kind) => {
944 let future = match nx_proof_kind {
946 NxProofKind::Nsec => handler.nsec_records(query.name(), lookup_options),
947 NxProofKind::Nsec3 {
948 algorithm,
949 salt,
950 iterations,
951 opt_out: _,
952 } => handler.nsec3_records(
953 Nsec3QueryInfo {
954 qname: query.name(),
955 qtype: query.query_type(),
956 has_wildcard_match: false,
957 algorithm: *algorithm,
958 salt,
959 iterations: *iterations,
960 },
961 lookup_options,
962 ),
963 };
964
965 match future.await.map_result() {
966 Some(Ok(nsecs)) => Some(nsecs),
968 Some(Err(error)) => {
969 warn!(%error, request_id = _request_id, "failed to lookup nsecs for request");
970 None
971 }
972 None => {
973 warn!(
974 request_id = _request_id,
975 "unexpected lookup skip for request"
976 );
977 None
978 }
979 }
980 }
981 None => None,
982 }
983 }
984 #[cfg(not(feature = "__dnssec"))]
985 None
986 } else {
987 None
988 };
989
990 let future = handler.lookup(handler.origin(), RecordType::SOA, None, lookup_options);
991 match future.await.map_result() {
992 Some(Ok(soa)) => (nsecs, Some(soa)),
993 Some(Err(error)) => {
994 warn!(%error, "failed to lookup soa");
995 (nsecs, None)
996 }
997 None => {
998 warn!("unexpected lookup skip");
999 (None, None)
1000 }
1001 }
1002 };
1003
1004 message.metadata = response_meta;
1006
1007 if let Some(mut lookup_records) = answers {
1008 if let Some(adds) = lookup_records.take_additionals() {
1009 message.additionals.extend(adds.iter().cloned());
1010 }
1011
1012 let is_referral = lookup_records.iter().next().is_some_and(|r| {
1013 r.record_type() == RecordType::NS
1014 && query.query_type() != RecordType::NS
1015 && query.query_type() != RecordType::ANY
1016 });
1017
1018 if is_referral {
1019 message.authorities.extend(lookup_records.iter().cloned());
1020 } else {
1021 message.answers.extend(lookup_records.iter().cloned());
1022 }
1023 }
1024
1025 if let Some(ns_records) = ns {
1026 message.authorities.extend(ns_records.iter().cloned());
1027 }
1028
1029 if let Some(soa_records) = soa {
1030 message.authorities.extend(soa_records.iter().cloned());
1031 }
1032
1033 message
1034}
1035
1036async fn build_forwarded_response(
1038 response: Result<AuthLookup, LookupError>,
1039 request_meta: &Metadata,
1040 #[cfg(feature = "__dnssec")] can_validate_dnssec: bool,
1041 query: &LowerQuery,
1042 lookup_options: LookupOptions,
1043) -> Message {
1044 let mut response_meta = Metadata::response_from_request(request_meta);
1045 response_meta.recursion_available = true;
1046 response_meta.authoritative = false;
1047 let mut message = Message::new(
1048 response_meta.id,
1049 response_meta.message_type,
1050 response_meta.op_code,
1051 );
1052 message.add_query(query.original().clone());
1053
1054 if !request_meta.recursion_desired {
1055 info!(
1056 id = request_meta.id,
1057 "request disabled recursion, returning REFUSED"
1058 );
1059
1060 response_meta.response_code = ResponseCode::Refused;
1061 message.metadata = response_meta;
1062 return message;
1063 }
1064
1065 enum Answer {
1066 Normal(AuthLookup),
1067 NoRecords(AuthLookup),
1068 }
1069
1070 #[cfg_attr(not(feature = "__dnssec"), allow(unused_mut))]
1071 let (mut answers, authorities, additionals) = match response {
1072 #[cfg(feature = "resolver")]
1073 Ok(AuthLookup::Resolved(lookup)) => {
1074 let answers =
1076 AuthLookup::answers(LookupRecords::Section(lookup.answers().to_vec()), None);
1077 let authorities =
1078 AuthLookup::answers(LookupRecords::Section(lookup.authorities().to_vec()), None);
1079 let additionals =
1080 AuthLookup::answers(LookupRecords::Section(lookup.additionals().to_vec()), None);
1081
1082 (Answer::Normal(answers), authorities, additionals)
1083 }
1084 Ok(l) => (
1085 Answer::Normal(l),
1086 AuthLookup::default(),
1087 AuthLookup::default(),
1088 ),
1089 Err(e) if e.is_no_records_found() || e.is_nx_domain() => {
1090 debug!(error = ?e, "error resolving");
1091
1092 if e.is_nx_domain() {
1093 response_meta.response_code = ResponseCode::NXDomain;
1094 }
1095
1096 let authorities = if let Some(authorities) = e.authorities() {
1098 let authorities = authorities
1099 .iter()
1100 .filter_map(|record| {
1101 if record.name == **query.name() {
1105 debug!(
1106 query_name = %query.name(),
1107 ?record,
1108 "changing response code from NXDomain to NoError due to other record",
1109 );
1110 response_meta.response_code = ResponseCode::NoError;
1111 }
1112
1113 match record.record_type() {
1114 RecordType::SOA => None,
1115 _ => Some(record.clone()),
1116 }
1117 })
1118 .collect();
1119
1120 AuthLookup::answers(LookupRecords::Section(authorities), None)
1121 } else {
1122 AuthLookup::default()
1123 };
1124
1125 if let Some(soa) = e.into_soa() {
1126 let soa = soa.into_record_of_rdata();
1127 let record_set = Arc::new(RecordSet::from(soa));
1128 let records = LookupRecords::new(LookupOptions::default(), record_set);
1129
1130 (
1131 Answer::NoRecords(AuthLookup::answers(records, None)),
1132 authorities,
1133 AuthLookup::default(),
1134 )
1135 } else {
1136 (
1137 Answer::Normal(AuthLookup::default()),
1138 authorities,
1139 AuthLookup::default(),
1140 )
1141 }
1142 }
1143 #[cfg(all(feature = "__dnssec", feature = "recursor"))]
1144 Err(LookupError::RecursiveError(recursor::RecursorError::Net(NetError::Dns(
1145 DnsError::Nsec {
1146 response, proof, ..
1147 },
1148 )))) if proof.is_insecure() => {
1149 response_meta.response_code = response.response_code;
1150
1151 if let Some(soa) = response.soa() {
1152 let soa = soa.to_owned().into_record_of_rdata();
1153 let record_set = Arc::new(RecordSet::from(soa));
1154 let records = LookupRecords::new(LookupOptions::default(), record_set);
1155
1156 (
1157 Answer::NoRecords(AuthLookup::answers(records, None)),
1158 AuthLookup::default(),
1159 AuthLookup::default(),
1160 )
1161 } else {
1162 (
1163 Answer::Normal(AuthLookup::default()),
1164 AuthLookup::default(),
1165 AuthLookup::default(),
1166 )
1167 }
1168 }
1169 Err(e) => {
1170 response_meta.response_code = ResponseCode::ServFail;
1171 debug!(error = ?e, "error resolving");
1172 (
1173 Answer::Normal(AuthLookup::default()),
1174 AuthLookup::default(),
1175 AuthLookup::default(),
1176 )
1177 }
1178 };
1179
1180 #[cfg(feature = "__dnssec")]
1182 if can_validate_dnssec {
1183 match &mut answers {
1208 Answer::Normal(answers) => match DnssecSummary::from_records(answers.iter()) {
1209 DnssecSummary::Secure => {
1210 if request_meta.authentic_data || lookup_options.dnssec_ok {
1211 trace!("setting ad header");
1212 response_meta.authentic_data = true;
1213 }
1214 }
1215 DnssecSummary::Bogus if !request_meta.checking_disabled => {
1216 response_meta.response_code = ResponseCode::ServFail;
1217 *answers = AuthLookup::default();
1219 }
1220 _ => {}
1221 },
1222 Answer::NoRecords(soa) => match DnssecSummary::from_records(authorities.iter()) {
1223 DnssecSummary::Secure => {
1224 if request_meta.authentic_data || lookup_options.dnssec_ok {
1225 trace!("setting ad header");
1226 response_meta.authentic_data = true;
1227 }
1228 }
1229 DnssecSummary::Bogus if !request_meta.checking_disabled => {
1230 response_meta.response_code = ResponseCode::ServFail;
1231 *soa = AuthLookup::default();
1233 trace!("clearing SOA record from response");
1234 }
1235 _ => {}
1236 },
1237 }
1238 }
1239
1240 message.metadata = response_meta;
1241
1242 match answers {
1243 Answer::Normal(answers) => {
1244 message.answers.extend(answers.iter().cloned());
1245 }
1246 Answer::NoRecords(soa) => {
1247 message.authorities.extend(soa.iter().cloned());
1248 }
1249 }
1250 message.authorities.extend(authorities.iter().cloned());
1251 message.additionals.extend(additionals.iter().cloned());
1252
1253 message.maybe_strip_dnssec_records(lookup_options.dnssec_ok)
1255}
1256
1257#[cfg(all(test, feature = "resolver"))]
1258mod tests {
1259 use std::{net::Ipv4Addr, str::FromStr};
1260
1261 use super::*;
1262 use crate::net::runtime::TokioRuntimeProvider;
1263 use crate::proto::rr::rdata::NS;
1264 use crate::proto::{
1265 op::{MessageType, OpCode, Query},
1266 rr::{
1267 Name, RData, Record, RecordType,
1268 rdata::{A, SOA},
1269 },
1270 };
1271 use crate::resolver::lookup::Lookup;
1272 use crate::store::in_memory::InMemoryZoneHandler;
1273 use crate::zone_handler::AxfrPolicy;
1274
1275 #[tokio::test]
1276 async fn test_build_forwarded_response_preserves_sections() {
1277 let query = Query::query(Name::from_str("example.com.").unwrap(), RecordType::A);
1279
1280 let mut lookup =
1282 Lookup::from_rdata(query.clone(), RData::A(A(Ipv4Addr::new(192, 0, 2, 1))));
1283
1284 lookup.extend_authorities([Record::from_rdata(
1286 Name::from_str("example.com.").unwrap(),
1287 3600,
1288 RData::SOA(SOA::new(
1289 Name::from_str("ns.example.com.").unwrap(),
1290 Name::from_str("admin.example.com.").unwrap(),
1291 1,
1292 3600,
1293 1800,
1294 604800,
1295 86400,
1296 )),
1297 )]);
1298
1299 lookup.extend_additionals([Record::from_rdata(
1301 Name::from_str("ns.example.com.").unwrap(),
1302 3600,
1303 RData::A(A(Ipv4Addr::new(192, 0, 2, 2))),
1304 )]);
1305
1306 let auth_lookup = AuthLookup::Resolved(lookup);
1308
1309 let mut request_meta = Metadata::new(1234, MessageType::Query, OpCode::Query);
1311 request_meta.recursion_desired = true;
1312 let query_lower = LowerQuery::query(query);
1313
1314 let message = build_forwarded_response(
1315 Ok(auth_lookup),
1316 &request_meta,
1317 #[cfg(feature = "__dnssec")]
1318 false,
1319 &query_lower,
1320 LookupOptions::default(),
1321 )
1322 .await;
1323
1324 assert!(
1326 !message.answers.is_empty(),
1327 "Answers section should not be empty"
1328 );
1329
1330 let authorities_count = message.authorities.len();
1332 assert!(
1333 authorities_count > 0,
1334 "Authorities section should not be empty, got {} records",
1335 authorities_count
1336 );
1337
1338 let additionals_count = message.additionals.len();
1340 assert!(
1341 additionals_count > 0,
1342 "Additionals section should not be empty, got {} records",
1343 additionals_count
1344 );
1345 }
1346
1347 #[tokio::test]
1348 async fn test_build_authoritative_response_referral() {
1349 let origin = Name::from_str("example.com.").unwrap();
1350 let sub = Name::from_str("sub.example.com.").unwrap();
1351 let ns_name = Name::from_str("ns.example.com.").unwrap();
1352
1353 let ns_record = Record::from_rdata(sub.clone(), 3600, RData::NS(NS(ns_name)));
1354 let record_set = RecordSet::from(ns_record);
1355
1356 let auth_lookup = AuthLookup::Records {
1357 answers: LookupRecords::new(LookupOptions::default(), Arc::new(record_set)),
1358 additionals: None,
1359 };
1360
1361 let handler = InMemoryZoneHandler::<TokioRuntimeProvider>::empty(
1362 origin.clone(),
1363 ZoneType::Primary,
1364 AxfrPolicy::Deny,
1365 #[cfg(feature = "__dnssec")]
1366 None,
1367 );
1368
1369 let metadata = Metadata::new(0, MessageType::Query, OpCode::Query);
1370 let query = LowerQuery::from(Query::query(
1371 Name::from_str("www.sub.example.com.").unwrap(),
1372 RecordType::A,
1373 ));
1374
1375 let message = build_authoritative_response(
1376 Ok(auth_lookup),
1377 &handler,
1378 &metadata,
1379 LookupOptions::default(),
1380 0,
1381 &query,
1382 )
1383 .await;
1384
1385 assert!(message.answers.is_empty());
1386 assert!(!message.authorities.is_empty());
1387 assert_eq!(message.authorities[0].record_type(), RecordType::NS);
1388 }
1389}